PAT甲级刷题总结

四则运算与堆栈

  • 后缀表达式通过堆栈运算:运算数压入,运算符弹出两个运算数运算后压入结果,最终栈顶即为结果

  • 中缀表达式要通过堆栈转化为后缀表达式:

    • 运算数:输出

    • 运算符:如果优先级>栈顶运算符,压入该运算符;如果<=,弹出并输出栈顶运算符直到该运算符优先级>栈顶运算符,压入该运算符。

    • 左括号:优先级栈外最高,栈内最低。

    • 右括号:弹出并输出运算符直到遇到左括号。

    • 括号不输出

    最终弹出并输出栈内运算符

  • 与二叉树的关系

    先序得前缀,中序得中缀,后序得后缀

  /*dfs搜索+string的拼接*/
  /*输出中缀表达式加括号*/
  string dfs(int root)
  {
      if(tree[root].left==-1&&tree[root].right==-1) return tree[root].data;
      if(tree[root].left==-1&&tree[root].right!=-1) return "("+tree[root].data+dfs(tree[root].right)+")";
      if(tree[root].left!=-1&&tree[root].right!=-1) return "("+dfs(tree[root].left)+tree[root].data+dfs(tree[root].right)+")";
  }
  • 2019秋季
  /*Postfix Expression */
  string dfs(int root)
  {
      if(tree[root].L==-1&&tree[root].R==-1) return "("+tree[root].data+")";
      if(tree[root].L==-1&&tree[root].R!=-1) return "("+tree[root].data+dfs(tree[root].R)+")";
      if(tree[root].L!=-1&&tree[root].R!=-1) return "("+dfs(tree[root].L)+dfs(tree[root].R)+tree[root].data+")";
  }
  //output:
  (((a)(b)+)((c)(-(d))*)*)
  //思路:如需加括号,将所有情况列出来

查找

二分查找 O(logN)

1、有序
2、模板
    //解决“寻找有序序列第一个满足条件的元素的位置”问题
    //二分区间[left,right]
int solve(int left,int right){
    int mid;
    while(left<right){//找到唯一位置
        mid=(left+right)/2;
        if(条件成立){//满足条件元素位置>=mid
            right=mid;
        }
        else left=mid+1;
    }
    return left;
}
3、函数
#include<algorithm>
//返回指针/迭代器!!!!!first,last为指针
lower_bound(first,last,val);//[first,last)范围内第一个>=val的元素
upper_bound(first,last,val);//第一个>val

two pointers(利用数列递增的思想)复杂度O(n)

  • 序列合并问题(如多项式加法)
  • 序列中a+b=M问题

树的遍历

两种题型:dfs和利用先中后序遍历序列特点写递归

/*先序中序转后序*/
void post(int root,int left,int right){//left,right标记中序子列,root标记先序序列
    if(left>right) return;
    int i=left;
    while(i<=right&&pre[root]!=in[i])i++;
    post(root+1,left,i-1);
    post(root+1+i-left,i+1,right);
    printf("%d ",pre[root]);
}
/*先序(后序)中序转层序,利用vector+sort*/
struct node{
    int index,value;
};
bool cmp(node a,node b){
    return a.index<b.index;
}
vector<node> ans;
vector<int> post,in;
void pre(int root,int left,int right,int index)
{
    if(left>right) return;
    int i=left;
    while(i<=right&&post[root]!=in[i]) i++;
    int nl=i-left;
    int nr=right-i;
    ans.push_back({index,post[root]});
    pre(root-1-nr,left,i-1,index*2);
    pre(root-1,i+1,right,index*2+1);
}
post.resize(n+1);
in.resize(n+1);
pre(n,1,n,1);
sort(ans.begin(),ans.end(),cmp);
/*模板*/
/*先(后)中序建树*/
/*启发性题目:1143——BST和先序序列的关系、1151——BT的结构*/
void XXXorder(int root,int left,int right)//注意:root标记中序,left、right标记先(后)序
{
    if(left>right) return;//出口
    while(i<=right&&没有找到根) i++;
    XXXorder(左树)XXXorder(右树)}
/*dfs  注意剪枝(1103)*/
void dfs(int root,A,B,C)
{
    if(root==-1){
        更新A,B,C;
        return;//!!!!!
    }
    path.push_back(root);
    dfs(左树)dfs(右树);
    path.pop_back();
}
******几个想法:*******
/*1、可见树的遍历(除bfs)本质是递归的应用*/
/*2、注意先序的特点:先序为进入顺序*/
/*3、BST与先序:可知进入顺序,可由BST特点(比较*值大小*推知LCA(BT需由*序列位置*推知,用map)*/
/*4、BST与中序:中序为小->大排序*/
******几个题目:*******
110311151119(先后->中)??、1130(中缀表达式)、1135(dfs路径)、1143(BST与先序)、1151(BT树结构)

BST

/*二叉搜索树BST*/
/*查找、插入、建树、删除*/
struct node{
    int data;
    node *left,*right;
};
/*建树*/
node* build(node* root,int v)/*注意如果函数类型是void,要写作void build(node* &root)*/
{
    if(root==NULL){
        root=new node();//int* p=new int;
        root->data=v;
        root->left=root->right=NULL;
    }
    else if(v<=root->data) root->left=build(root->left,v);
    else root->right=build(root->right,v);
    return root;
}
node* root=NULL;//注意置NULL
for(int i=0;i<n;i++){
    int t;
    scanf("%d",&t);
    root=build(root,t);
}
/*删除以root为根节点,权值为x的结点*/
node* findmax(node* root)
{
    while(root->right!=NULL) root=root->right;
    return root;
}
void delete(node* &root,int x)
{
    if(root->data==x){
        if(root->left==NULL&&root->right==NULL)
            root=NULL;
        else if(root->left!=NULL){
            node* pre=findmax(root->left);
            root->data=pre->data;
            delete(root->left,pre->data);
        }
        else{
            node* next=findmin(root->right);
            root->data=next->data;
            delete(root->right,next->data);
        }
    }
    else if(root->data<x) delete(root->right,x);
    else delete(root->left,x);
}

并查集

/*并查集操作*/
/*main中千万别忘记initial()*/
int fa[n];
void initial(){
    for(int i=0;i<n;i++)
        fa[i]=-1;
}
int find(int x)
{
    if(fa[x]<0) return x;
    int F=find(fa[x]);
    fa[x]=F;
    return F;
}
void Union(int a,int b)
{
    int faA=find(a);
    int faB=find(b);
    if(faA!=faB){
        if(fa[faA]<fa[faB]){
          fa[faA]+=fa[faB];
          fa[faB]=faA;
        }
		else {
          fa[faB]+=fa[faA];
          fa[faA]=faB;
        }
  }
}

  • 结构特点:完全二叉树!!!!!

  • 最大堆、最小堆

    判别方法:

    /*判断是大顶堆还是小顶堆*/
    for(int i=2;i<=n;i++){
        if(a[i/2]>a[i]) isMin=0;
        if(a[i/2]<a[i]) isMax=0;
    }
    if(isMin==1&&isMax==0) printf("Min Heap\n");
    else printf("%s",isMax==1&&isMin==0?"MaxHeap":"Not Heap");
    
  • 堆的建立

    typedef struct HNode *Heap; /* 堆的类型定义 */
    struct HNode {
        ElementType *Data; /* 存储元素的数组 */
        int Size;          /* 堆中当前元素个数 */
        int Capacity;      /* 堆的最大容量 */
    };
    #define MAXDATA 1000  /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */
    MaxHeap CreateHeap( int MaxSize )
    { /* 创建容量为MaxSize的空的最大堆 */
     
        MaxHeap H = new HNode();
        H->Data = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType));
        H->Size = 0;
        H->Capacity = MaxSize;
        H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/
     
        return H;
    }
    void PercDown( MaxHeap H, int p )
    { /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
        int Parent, Child;
        ElementType X;
        X = H->Data[p]; /* 取出根结点存放的值 */
        for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
            Child = Parent * 2;
            if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
                Child++;  /* Child指向左右子结点的较大者 */
            if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
            else  /* 下滤X */
                H->Data[Parent] = H->Data[Child];
        }
        H->Data[Parent] = X;
    }
    void BuildHeap( MaxHeap H )
    { /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
      /* 这里假设所有H->Size个元素已经存在H->Data[]中 */
        int i;
        /* 从最后一个结点的父节点开始,到根结点1 */
        for( i = H->Size/2; i>0; i-- )
            PercDown( H, i );
    }
    

概述

图的题目分为:

  • dfs、bfs遍历或并查集判断连通性1021、1034、1076(bfs)

  • dfs解决无权图最短路径问题1131(易与上一点代码混淆)

  • dijkstra+dfs解决有权图最短路径问题1018(dijkstra不熟练,找bug时间长)

  • 考察图的定义和特点 1139?、1142、1154(逻辑不清晰、做题过程混乱)

    图不够熟练,平均做题速度>50min

图的遍历

/*深度优先搜索遍历图*/
/*伪码*/
dfs(u)
{
    vis[u]=true;
    for(从u出发能到达的所有顶点v)
        if(vis[v]==false)
            dfs(v);
}
dfstraversal(G)
{
    for(G的所有顶点u)
        if(vis[u]==false)
            dfs(u);
}
/*邻接矩阵*/
void dfs(int u,int depth)
{
    vis[u]=true;
    //对u的操作  
    for(int v=0;v<n;v++){
        if(vis[v]==false&&G[u][v]!=inf)
            dfs(v,depth+1);
    }
}
void dfstraversal()
{
    for(int u=0;u<n;u++)
        if(vis[u]==false)
            dfs(u,1);
}
/*邻接表*/
void dfs(int u,int depth)
{
    vis[u]=true;
    for(int v=0;v<G[u].size();v++)
        if(vis[G[u][v]]==false) dfs(G[u][v],depth+1);
}
/*BFS*/
/*伪码描述*/
BFS(u)
{
    queue q;
    q.push(u);
    inq[u]=true;//入队即标记
    while(q.empty()==false){
        访问队首元素;
        q.pop();
        for(从u出发可到达的所有顶点v)
           if(inq[v]==false){//注意是把没入队的加入而不是没访问过的加入
                q.push(v);
                inq[v]=true;
           }
    }
}
BFSTraversal(G)
{
    for(G的所有顶点u)
       if(inq[u]==false)
          BFS(u);
}
/*带层数的时候*/
struct node{
    int v,layer;
};
next.layer=top.layer+1;

dfs解决无权图最短路径

vector<int> temp,ans;
void dfs(int index,标尺A,B,C)
{
    if(枚举结束){
        if(该方案能使A,B,C条件更优)
            更新结果
        return;
    }
    if(剪枝条件) return;
    temp.push_back(index);
    dfs(index+1,更新A,B,C);//选index的情况,如果index可重复选择,则改为index
    temp.pop_back();
    dfs(index+1,A,B,C);//不选index的情况
}
/*在图中求满足条件的方案并输出路径时*/
vector<int> temp;//记录临时路径
void dfs(int start,int end1,int depth)
{
    if(start==end1){
        temp.push_back(start);
        /***/
        temp.pop_back();
        return;
    }
    vis[start]=true;
    temp.push_back(start);
    for(int i=0;i<G[start].size();i++)
        if(vis[G[start][i]]==false) dfs(G[start][i],end1,depth+1);
    vis[start]=false;//!!!!!注意与遍历图中所有节点算法的不同!A1131
    temp.pop_back();  
}

Dijkstra求有权图最短路径

/*不能求带负权边的最短路径*/
/*dfs可以求一个点到另一个点的最短路径的长度*/
/*伪码*/
void dijkstra(int s)
{
    初始化;
    for(循环n次){
        u=使d[u]最小的还未被访问的顶点标号;
        vis[u]=true;
        for(从u出发能到达的所有顶点v)
            if(v未被访问过且以u为中介能使d[v]更优)
                优化d[v];
    }
}
/*邻接矩阵*/
/*Dijkstra&DFS*/
#include<algorithm>/*fill函数头文件*/
using namespace std;
const int INF=0x3fffffff;
vector<int> path[maxN];
bool vis[maxN];
int dis[maxN];
int G[maxN][maxN],N;
//初始化图在main中已完成
void Dijkstra(int s)
{
    fill(dis,dis+maxN,INF);
    fill(vis,vis+maxN,false);
    dis[s]=0;//注意赋值时s不是0
    for(int i=0;i<N;i++){
        int u=-1;int mind=INF;
        for(int j=0;j<N;j++)
            if(vis[j]==false&&dis[j]<mind){
                u=j;
                mind=dis[j];
            }
        if(u==-1) return;//not connect
        vis[u]=true;
        for(int v=0;v<N;v++){
            if(vis[v]==false&&G[u][v]!=INF){//attention!!!不能写入循环条件中,注意是vis[v]
                if(d[u]+G[u][v]<d[v]){
                    d[v]=d[u]+G[u][v];
                    path[v].clear();
                    path[v].push_back(u);
                }
                else if(d[u]+G[u][v]==d[v]){
                    path[v].push_back(u);
                }
            }
        }
    }
}
int optvalue;
vector<int> temp,ans;
void DFS(int v)
{
    if(v==s)//s为起点
    {
        temp.push_back(v);
        int value;
        /*计算temp路径的value*/
        if(value优于optvalue){
            optvalue=value;
            ans=temp;
        }
        temp.pop_back();
        return;
    }
    temp.push_back(v);//**
    for(int i=0;i<path[v].size();i++)//注意是path哦,不是G
      DFS(path[v][i]);//注意别写错
    temp.pop_back();
}
/*邻接表*/
struct node{
    int v,d;
};
vector<node> G[maxn];
int n;
int dis[maxn];
bool vis[maxn];
/.../
//与邻接矩阵唯一不同的地方
for(int i=0;i<G[u].size();i++){
    int v=G[u][i].v;//
    if(vis[v]==false&&dis[u]+G[u][i].d<dis[v]){//有标尺的时候要把vis[v]==false放外面
          dis[v]=dis[u]+G[u][i].d;
    }
}
/*无需输出路径的dijkstra和其标尺统计*/
fill(num,num+maxn,0);
fill(vw,vw+maxn,0);
for(int j=0;j<G[u].size();j++){
    int v=G[u][j].v;
    if(vis[v]==false){
         if(d[u]+G[u][j].weight<d[v]){	 
            d[v]=d[u]+G[u][j].weight;
            num[v]=num[u];
            vw[v]=vw[u]+teams[v];
        }
        else if(d[u]+G[u][j].weight==d[v]){
          num[v]+=num[u];//路径数统计
          if(vw[u]+teams[v]>vw[v])//点权
             vw[v]=vw[u]+teams[v];
        }
    }
}

图的连通性判断(DFS或并查集)

/*DFS*/
vector<int> G[maxN];
bool vis[maxN]={false}
int occupy;
void DFS(int u)
{
    if(u==occupy) return;//delete the occupied node
    vis[u]=true;
    for(int i=0;i<G[u].size();i++)
       if(vis[G[u][i]]==false)
          DFS(G[u][i]);
}
int block=0;
for(int i=1;i<=N;i++){
    if(i!=occupy&&vis[i]==false){
        DFS(i);
        block++;
    }
}
/*并查集*/
for(int u=1;u<=N;u++){
    for(int v=0;v<G[u].size();v++){
       if(u==occupy||G[u][v]==occupy) continue;//跳过被occupy的点
       Union(u,G[u][v]);
    }
}
int block=0;
for(int i=1;i<=N;i++){
    if(i==occupy) continue;
    int fa_i=father(i);
    if(vis[fa_i]==false) block++;
    vis[fa_i]=true;
}

拓扑排序

void TopSort()
{ 
    for(图中每个顶点 V){
        if(Indegree[v]==0)
            Enqueue(v,Q);
    }
    while(!isEmpty(Q)){
        v=Dequeue(Q);
        输出V;
        cnt++;
        for(V的每个邻接点W)
            if(--Indegree[w]==0)
                Enqueue(W,Q);
    }
    if(cnt!=|V|) Error("图中有回路");
}

C++

输入输出

  • double 输入%lf 输出%f long double 输入出%Lf

  • 输入一行字符串带空格:

    **如果是string s 则用getline(cin,s);头文件#include<string> **

    如果是char str[100]则用cin.getline(str,100);头文件#include<iostream>;若该行前面有不需要部分,可先单独输入不需要部分,再输入getline

  • !!!!!getline(cin,s)前面有换行输入时,一定要在前面加上getchar()或者scanf

  • %c会读入空格回车

  • sscanfsprintf:

    • 头文件#include<stdio.h>

    • 作用:字符数组str[100]"%d:%lf,%s"的转换

    • #include<stdio.h>
      char str[100]="2020:3.14,hello".str2[100];
      sscanf(str,"%d:%lf,%s",&n,&db,&str2);
      printf(n,db,str2);
      sprintf(str,"%d:%.2f,%s",n,db,str2);
      printf(str);
      /*如果str是string类型*/
      sscanf(str.c_str(),"",)
      
    • 四舍五入取整:+0.5后(int)

math.h头文件

log(double)以e为底

ceil(double)向上取整 floor向下取整

fabs(double x)

algorithm头文件

fill()
reverse(string.begin(),string .end());
sort(a,a+n,greater<int>());
swap(a,b);
max(x,y)/min(x,y)//x,y同类型
abs(int x)//fabs(double)
lower_bound(first,last,val)//>=val
upper_bound(first,last,val)//>val

cctype头文件

tolower(char c);
isalpha(char c);
isalnum(char c);

STL

string

/*substr的用法*/
string s2=s.substr(4);//从下标4到结束
string s3=s.substr(5,3)//从下标5开始,3个
/*find*/
string.find(str,pos)//返回下标值,如果没有==string::npos
    if(s.find('a',5)!=string::npos)
        int index=s.find('a',5);
s.find('a');//返回a第一次出现的下标
s.find('a',5);//从下标5开始,寻找a第一次出现的下标
/*erase*/
s.erase(pos,cnt);
s.erase(s.begin()+n);
/*insert*/
s.insert(pos,string);//注意:insert到pos前!!!!
/*string转int*/
stoi(string);//string可带“+/-”
/*int/double/float转string*/
string s=to_string(int/double/float);

map(按照键从小到大排序)

map<> mp;
for(auto it=mp.begin();it!=mp.end();it++)

vector

vector的拼接
vector<int> v1,v2;
v1.insert(v1.end(),v2.begin(),v2.end());//将v2拼接在v1后面
v1.insert(v1.begin(),v2.begin(),v2.end());//将v2放在v1前面

运算符重载

struct node{
    int index,freq;
    bool operator <(const node &a) const{
        return (freq!=a.freq)?freq>a.freq:index<a.index;
    }
};

set

set.size();

引用和传参

修改函数传入的参数,可用指针或引用。

对引用变量的操作即为对原变量的操作

bool cmp(node &a,node &b)//排序传参建议⽤引⽤传参

简单数学

素数的判断及素数表

bool isprime(int n)
{
    if(n<=1) return false;
    int sqr=(int)sqrt(n*1.0);
    for(int i=2;i<=sqr;i++)
        if(n%i==0) return false;
    return true;
}
//素数表
//初始化标记为1(是素数),外层循环从2到sqrt(n),内层循环从2到j*i<n(把j的i倍全部标记为0)
vector<int> isprime(50000,1);
for(int i=2;i*i<50000;i++)
    for(int j=2;j*i<50000;j++)
        isprime[j*i]=0;

大整型运算

struct bign{
    int d[maxn],len;
    bign(){fill(d,d+maxn,0);len=0;}
};
bign change(string s)
{
    bign a;
    for(int i=0;i<s.length();i++)
        a.d[a.len++]=s[s.length()-1-i]-'0';
    return a;
}
bign add(bign a,bign b)
{
    bign c;
    int carry=0;
    for(int i=0;i<a.len||i<b.len;i++){
        int tmp=a.d[i]+b.d[i]+carry;
        c.d[c.len++]=tmp%10;
        carry=tmp/10;
    }
    if(carry!=0) c.d[c.len++]=carry;
    return c;
}
//减法、乘法、除法!!!!!

分数及其四则运算

//**结构**//
struct node{
    long int up,down;
};
//**化简**//
//**三种情况**//
long int gcd(long int a,long int b)
{
    if(b==0) return a;
    else return gcd(b,a%b);
}
node reduct(node r)//分母不为0
{
    if(r.down<0){r.up=-r.up;r.down=-r.down;}
    if(r.up==0) r.down=1;
    else{
        long int d=gcd(abs(r.up),r.down);//abs!!!!!
        r.up/=d;r.down/=d;
    }
    return r;
}
/*四则运算:除法要注意分母不为0*/
void output(node r)
{
    r=reduct(r);
    if(r.down==1) printf("%ld",r.up);
    else if(abs(r.up)>r.down) 
        printf("%ld %ld/%ld",r.up/r.down,abs(r.up)%r.down,r.down);
    else printf("%ld/%ld",r.up,r.down);
}

质因子分解

/*质因子分解*/
struct factor{
    int x,cnt;
}fac[10];//对于int型来说数组大小为10即可
/*根据结论:对一个正整数来说,其质因子最多只有一个>sqrt(n),其余均<=sqrt(n)*/
1、枚举[2,sqrt(n)]内n的质数,判断其是否是n的质因子,设为p,加入factor结构体,初始化cnt=0;
只要p还是n的因子,令n不断除p,cnt++,直到p不再是n的因子;
if(n%prime[i]==0){
    fac[num].x=prime[i];
    fac[num].cnt=0;
    while(n%prime[i]==0){
        fac[num].cnt++;
        n/=prime[i];
    }
    num++;
}
2、若上述枚举结束后n>1,则说明n有一个>sqrt(n)的因子;
if(n!=1){
    fac[num].x=n;
    fac[num].cnt=1;
}

哈希表

/*哈希映射+平方探测法*/
/*插入*/
int flag=0;
for(int i=0;i<=tsize;i++)//插入次数<=tsize+1
{
    int pos=(a+i*i)%tsize;
    if(v[pos]==0){
        v[pos]=a;
        flag=1;
        break;
    }
}
if(flag==0) printf("cannot be inserted");
/*计算查找时间*/
int cnt=0;
for(int i=0;i<=tsize;i++)//
{
    cnt++;
    int pos=(a+i*i)%tsize;
    if(v[pos]==a||v[pos]==0) break;
}

错误集锦

  • 运行超时

    cout、map易超时 A1053
    
    
  • 二维数组最大开10000

  • long doublescanf("%Lf") printf("Lf")
    

Tips

  • vector之间可以赋值

  • 10^9以内或32bit——int

  • long long(64bit)输入输出%lld

  • getline()若有其他输入一定要scanf("%d\n",&n);输入\n

  • 链表:注意可能输入数据有无用点,故输入的数据数可能不是有效结点数(A1074)

  • 注意统计条件:

    • A1071:非alnum间隔有效字符串,注意最后一个有效字符串需要特殊考虑(因为后面没有非alnum了)
  • do while()的运用 A1069

  • 溢出知识的运用:A1065

  • distinct numbers不同的数字

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值