PAT算法问题模板
常用语法
函数库ctype.h
-
大写变小写
tolower(char c);返回c的小写 -
小写变大写
toupper(char c);返回c的大写
总结:一次只能用于一个字母 -
判断字符是否是字母
isalpha(char c);返回bool -
判断是否为数字
isdigit(char c);返回bool -
判断字符是否为字母和数字
isalnum(char c);返回bool -
判断字符是否为大写或者小写字母
isupper(char c),islower(char c);返回bool
函数库algorithm
1.max_element(),min_element();
返回迭代器,默认是从小到大排序,max返回最后一个元素,min返回第一个元素。可以自己构造cmp函数,让它排序
2.reverse(iterator a,iterator b);将两个迭代器之间的元素翻转,b是所指元素的下一个位置。
for语句,范围for
-
除了一般的循环,还有遍历功能,如
for(声明:序列),然后就会遍历了。
分组
1.对于一个序列,把它分组。
int temp = np,group;//temp为当前序列长度
while(a.size()!=1)
{
if(temp%ng==0) group = temp/ng;//先计算可以分出多少组
else group = temp/ng + 1;
for(int i=0;i<group;i++)//外层组循环
{
int k = a.front();
for(int j=0;j<ng;j++)//内层组内循环
{
if(i*ng+j>=temp) break;//元素遍历多了,自动跳出,最后一个组元素不够
int front = a.front();
if(m[k].w<m[front].w)
{
k = front;
}
m[front].r = group + 1;
a.pop();
}
a.push(k);
}
temp = group;
}
stl模板库
vector
定义
- vector< typename> v;
- vector< typename> v(n); 定义一个vector,包含n个元素,都默认初始化。
- vector< typename> v(n,1);定义一个长度为n的向量,里面的元素初始值为1。
- vector< typename>
- vector< typename> v1(v.begin(),v.end());定义了一个v1,里面的内容为v,其实也可以复制一部分。括号里面必须是地址或者迭代器。
使用
- v.resize(n),把vector设置为长度为n,并且初始化。
string
定义
- string s;
使用
- 常用函数to_string(),将常用格式转化成为string格式
- string可以加减和赋值以及比较。前加和后加操作灵活。
- stoi可以将string化为整数返回。
- string.rbegin(),string.rend();反向迭代器。
- string.substr(pos,len);pos是位置,len是长度。pos不是迭代器。不能用迭代器。
- string.erase(iterator);删除单个元素,string.erase(pos,len);从pos开始删除len长的子串string.erase(pos);从pos开始,后面全部删除。string.erase(it1,it2),删除两个迭代器之间的元素。
常见算法
树
1.前序和中序遍历建树
void creat(node* &root,int prel,int prer,int inl,int inr)
{
if(prel>prer) return;
int u = pre[prel];
root = newnode(u);
int k;
for(int i=inl;i<=inr;i++)
{
if(u==in[i])//在中序序列中找到这个根节点
{
k = i;
break;
}
}
int numleft = k-il;
creat(root->left,prel+1,prel+numleft,inl,inl+numleft-1);
creat(root->right,prel+numleft+1,prer,inl+numleft+1,inr);
}
2.AVL平衡二叉树
节点模板
平衡二叉树重要的是要记录高度,用来算平衡因子
struct node{
int val,height;
node* left,*right;
};
构建二叉树必要的一些模块化函数:
node* newnode(int v)//建立节点
int getheight(node* root)//得到节点高度
int getbalancefactor(node* root)//得到节点的平衡差值
void updateheight(node* root)//节点变动之后,更新节点参数
void L(node* &root)//左旋
void R(node* &root)//右旋
void insert(node* &root,int x)//插入节点
函数的具体实现
node* newnode(int v)
{
node* now = new node;
now->height = 1;
now->val = v;
now->left = NULL;
now->right= NULL;
return now;
}
int getheight(node* root)
{
if(root==NULL) return 0;
return root->height;
}
int getbalancefactor(node* root)
{
//获得左右子树的高度差
return getheight(root->left)-getheight(root->right);
}
void updateheight(node* root)
{
root->height = max(getheight(root->left),getheight(root->right))+1;//要是和getheight互换,那么每次查询高度差都要计算,可能会超时。更新计算
}
void L(node* &root)
{
node* temp = root->right;
root->right = temp->left;
temp->left = root;
updateheight(root);
updateheight(temp);
root = temp;
}
void R(node* &root)
{
node* temp = root->left;
root->left = temp->right;
temp->right= root;
updateheight(root);
updateheight(temp);
root = temp;
}
void insert(node* &root,int x)
{
if(root==NULL)
{
root = newnode(x);
return;
}
if(x < root->val)
{
insert(root->left,x);
updateheight(root);
if(getbalancefactor(root)==2)
{
if(getbalancefactor(root->left)==1)
{
R(root);
}else
{
L(root->left);
R(root);
}
}
}else{
insert(root->right,x);
updateheight(root);
if(getbalancefactor(root)==-2)
{
if(getbalancefactor(root->right)==-1)
{
L(root);
}else
{
R(root->right);
L(root);
}
}
}
}
3.并查集
常用结构
father[maxn];数组,指向父亲下标。开始的时候都指向自己。是一个集合的概念。
常用的算法操作
init();初始化father数组,每个的结点都指向自己
findfather(int x);找到x结点的根节点。最好把路径上的结点都更新指向根节点。
Union(int a,int b);若a,b不在同一个集合,合并他们。
算法实现
void init()
{
for(int i=1;i<maxn;i++)
{
father[i] = i;
}
}
int findfather(int x)
{
if(x==father[x]) return x;
else
{
int F = findfather(father[x]);
father[x] = F;
return F;
}
}
void Union(int a,int b)
{
int fa = findfather(a);
int fb = findfather(b);
if(fa!=fb)
{
father[fa] = fb;
}
}
并查集的应用
常用于判断哪些是属于一个集合。
4.堆的使用
常用结构
数组heap[N+1]或者vector heap[N+1];
堆是一颗特殊的数,通常用层序的方法来存,所以从1到N来存,方便处理
常用算法操作
downadjust(int low,int high);将上方的点往底层调整
upadjust(int low,int high);将下方的点往顶层调整
creat();创建堆。
选向下调整复杂度为O(n) ,从n/2开始往1一个一个调整
选向上调整复杂度为O(nlogn),从2开始往n一个一个调整
heapsort();堆排序。若要从小到大排序,就建立大顶堆,大到小排序,就建立小顶堆。
deletetop();删除堆顶元素
insert(int x);添加元素
算法实现
void downjust(int low,int high)
{
int i = low,j = i*2;
while(j<=high)
{
//找出两个孩子结点中的最大值
if(j+1<=high&&heap[j+1]>heap[j])
{
j = j+1;
}
//如果孩子结点中的最大值比欲调整的结点大
if(heap[j]>heap[i])
{
swap(heap[i],heap[j]);
i = j;
j = j*2;
}else
{
break;//若有一个不满足,那么证明后面的都已经调整好了
}
}
}
void upadjust(int low,int high)
{
int i =high,j = i/2;
while(j>=low)
{
//父亲权值小于欲调整的结点i的权值
if(heap[j]<heap[i])
{
swap(heap[j],heap[i]);
i = j;
j = j/2;
}else
{
break;
}
}
}
void creat()
{
for(int i=N/2;i>0;i--)
{
downjust(i,N);//自底向上复杂度低
}
}
void heapsort()
{
for(int i=N;i>1;i--)
{
swap(heap[1],heap[i]);
downjust(1,i-1);
}
}
void deletetop()
{
heap[1] = heap[n--];
downadjust(1,n);
}
void insert(int x)
{
heap[++n] = x;
upadjust(1,n);
}
堆的应用
常用于排序题。排序的复杂度为O(nlogn)
归并排序
算法实现
void MergeSort(vector<int> &v)
{
bool flag = false;
for(int step=2;step/2<=N;step*=2)
{
for(int i=0;i<N;i+=step)
{
sort(v.begin()+i,v.begin()+min(i+step,N));
}
if(flag)
{
break;
}
if(v==v2)
{
flag = true;
}
}
return;
}
归并排序的应用
题目中一般是一步一步的,但是用sort和真正的归并排序不一样,真正的是先左边再右边,而用hash是不管左右的,都是以2的幂次方分组的。
Hash表
1.平方探测法
建表
for(int i=0;i<tsize;i++)//这个是只有正方向的平方探测法,建立最多tsize次
{ //tsize是表的尺寸
int pos = (a + i*i)%tsize;//a是插入的元素,pos是插入的位置
if(hashtable[pos]==0)//找到一个空位置
{
hashtable[pos] = a;
break;
}
}
查询
for(int i=0;i<=tsize;i++)//这个最多要查询tsize+1次
{
int pos =(a+i*i)%tsize;//a为要查询的数字,pos是位置
if(hashtable[pos]==a||hashtable[pos]==0)//无论是找到了数字,还是当前位置为空查找都结
{ //束了
break;
}
}
- 注意:建立最多循环tsize次,查询最多tsize+1次。而且查询结束有两种情况,找到算一种。表中元素为空,代表这个元素
没在hash表中。因为这个位置为空,平方探测法计算的下一个位置也一定为空。
动态规划
LIS(最长不下降子序列)
方法1
dp[i] 表示以A[i]结尾的最长不下降子序列长度。
状态转移方程
dp[i] = max{1,dp[j] + 1}
(j = 1,2…,i-1&&A[ j ] < A[ i ])
算法思想:对于每个元素,在元素 i 前面找标号小于它的元素,这些元素里面有最长子序列长度的,元素 i 接在他后面,若前面所有元素的标号都大于他,那么他自己单独作为最长不下降子序列,长度为1.
for(int i=1;i<=n;i++)
{
dp[i] = 1;
for(int j=1;j<i;j++)
{
if(A[i]>=A[j]&&(dp[j]+1>dp[i}){
dp[i] = dp[j] +1;
}
}
ans = max(ans,dp[i]);//最终的ans就是答案。
}
方法2
dp[i] 表示在子序列中下标为i的元素结尾的当前序列的最长不下降子序列长度。
状态转移方程
dp[i]++
for(int j=i+1;j<N;j++)
dp[j] = max{dp[j],dp[j-1]};
算法思想:最开始的时候dp[]数组里面全部为0,当第一个元素来的时候,对应的下标数组元素+1,大于这个下标的元素可以选择接在原先的序列后面,或者这个新元素后面。所以取最大值。
动态查询
分块查询
方法
建立两个数组,一个存每个数出现的次数,一个存块内元素出现的次数
树状数组
用的函数与数据结构
c[maxn] c[i] 存的是小于i,范围为lowbit(i)范围数据的多少
lowbit(i) ((i)&(-i)) 可以用宏定义
update(int x,int num);更新元素x的相关数组,更新的增量为num
getsum(int x);得到小于等于x的元素个数;
函数的具体实现
int c[maxn];
#define lowbit(i) ((i)&(-i))
void update(int x,int num)
{
for(int i = x;i<maxn;i+=lowbit(i))
c[i]+=num;
}
int getsum(int x)
{
int sum = 0;
for(int i=x;i>0;i-=lowbit(i))
sum+=c[i];
return sum;
}