PAT甲级题目解析和知识点分类整理

转载请注明出处
个人博客:https://maxusun.github.io/

今天整理电脑,发现了去年为了考研浙大计算机整理的PAT资料。现在考研已经尘埃落定。想到当时盲目刷题浪费了好多时间,在这里希望将整理的东西放到网上,方便后来者学习。

PAT的命题趋势

将PAT甲级中的题目从第一题刷到最后一题,可以明显感受到,PAT甲级的题目风格发生了很明显的变化。从早期的模拟型题目变成了后来以数据结构和算法为主的固定题型。如果时间紧迫的话,可以只刷后一般的题目,放弃前一半的题目,因为前一半题目的出题风格已经过时了。

PAT题目按分数和知识点分级

PAT按照分数分为20,25,30分。下面是将题目按照这3个分数段进行整理。
其中25分和30分的题目代码在这里:代码

20分段

20分的部分题目没有标注涉及的知识点,没有标注的这些题目说明非常简单,只是用来熟悉编程语言的,稍微有些基础的同学可以跳过,没有基础的同学可以先做这些题目。

题号知识点是否建议二刷
1001
1005
1008
1011
1015
1019
1023
1027
1031
1035
1041
1042
1046
1050
1054
1058
1061
1065
1069
1073
1088分数处理
1092
1096
1100
1104
1108字符串于浮点数间的转换二刷
1112字符串处理可二刷
1116还行,hash
1120
1124
1128n皇后问题,但是简化了,只是让验证而已
1132
1136字符串处理数字加减可二刷
1140字符串处理二刷
1144
1148狼人杀
1152字符串处理(自己想麻烦了)二刷

25分段

25分的题目比20分的题目多了解决方法和备注。没有标注解决方法的题目是因为比较简单或者涉及的解决方法已经在其他题目中使用。

题号知识点解决方法是否建议二刷备注
1002模拟多项式加
1003最短路径+点权+最短路径数量
1006查找元素
1007DP最大连续子序列可二刷
1009时间处理
1010二分法基数数字转换这里如果不使用二分法,只有一个测试点过不去
1012排序结构体排序亮点在,对四门课同样的排序可以只写一个cmp,结构体中用数组即可
1013无向图的连通分量
1016排序
1017模拟银行
1020二叉树查找树
1021无向图+连通分量+最深结点*2
1024大整数
1025排序
1028排序
1029two pointers
1032链表
1033贪心
1036查找元素
1037贪心
1039STL应用
1040DP
1043二叉查找树
1044二分查找
1047STL应用
1048散列
1051判断序列是否为出栈序列二刷这里可用这种最笨的方法来输出所有栈的结果
1052链表模拟静态链表,有点坑二刷里面有很多好的写法
1055排序结构体排序有点水
1056队列可二刷题目不是多难,就是开始想岔了,没读懂题目
1059素数表建立数的素数分解二刷这题变形可出关于正整数的因子个数
1060科学计数法
1062排序排序,cmp+sort
1063set,STL应用set应用
1066aviAVI的建立可二刷这次使用的是静态链表方法
1067贪心贪心+排序二刷挺绕的一题
1070贪心贪心有一个测试用例没过,然后发现因为float的数据被我认为是int了
1071map,STL字符串处理这里使用两个字符串统计单词,要比之前我只用索引来确定方便很多
1074链表静态链表反转二刷数据和地址分离,直接反转地址,太特么秀了
1078hashHash二次探测法
1079树+找叶结点+深度
1082字符串处理数字的中国传统读法
1083排序排序水题
1085二分法
1086先序中序转后序
1089判断排序方法二刷
1090树+找叶结点+深度
1093逻辑题
1094树+最多结点层数
1097链表链表分割可二刷仍然是用那种无赖的方法,很爽
1098判断排序方法堆排序+插入排序与1089一起二刷
1101快排快排特点
1102树+层序+中序indices:指数
1105模拟打印螺旋形矩阵二刷
1106树+DFS+叶子最小的层可以二刷
1109逻辑题拍照站位不知道为啥错
1110完全二叉树判断一个树是不是完全二叉树可二刷
1113排序+贪心贪心有点水
1115二叉查找树二叉查找树+静态构建二刷(必须)
1114并查集并查集统计很多二刷
1117逻辑题读懂题意就较为简单
1118并查集并查集统计集合和元素个数二刷
1121set应用hash用法
1122哈密顿环二刷可以再做,被自己写复杂了
1125贪心贪心,排序题目不难,就是浮点数向整数舍入有点恶心
1126欧拉图这里需要判断图是否连通
1129set应用,运算符重载
1130表达式树+中序
1133链表模拟链表处理链表处理不能忘了去不用的结点,并且有种非常无赖的方法
1134hashhash_set这题如果使用set,则200多毫秒,如果使用hash_set则快些
1137map映射,排序float转int四舍五入:round 向上取整:ceil 向下取整:floor,强转
1138二叉树先序+中序转后序可二刷这里需要剪枝,另外这种代码可以不建树,直接由建树代码改造
1141map,排序map,STL的应用这里有个坑:浮点转整形时,计算sum时,将sum全计算完再做,若每次都转再相加,精度严重丢失 case insensitive:不区分大小写
1142找完全图
1145哈希Hash二次探测法+查找二刷
1146topo排序二刷可以再做,被自己写复杂了
1149STL应用set应用水题
1150旅行商问题+哈密顿变体
1153排序,模拟排序二刷答案的结构体设置特别好
1154hash 图图+边两端结点颜色不同注意如何存储一个一个的边

30分段

30分的题目多了解决方法和备注。没有标注解决方法的题目是因为比较简单或者涉及的解决方法已经在其他题目中使用。

题号知识点解决方法是否建议二刷备注
1004树遍历
1014队列
1018DFS+Dijkstra+三value二刷
1022map映射,STL使用
1026模拟,排序
1030Dijkstra+二value
1034DFS+连通分量+分量所有边权和
1038贪心
1045DP
1049数学
1053树遍历DFS找路可二刷
1057树状数组使用树状数组求序列中第k大数据二刷
1064二叉查找树在构建好的完全二叉树上先序遍历+层序遍历可二刷(较水)
106801背包,DP
1072Dijkstra+三value二刷atoi需要stdlib包
1076BFS+统计多少层以内结点数目可二刷
1080排序
1087Dijkstra+DFS+三value可二刷比较耗时间
1091三维bfs
1095map,排序
1099二叉查找树静态二叉查找树构建二刷
1103深度优先搜索DFS
1107并查集使用并查集即可可二刷
1111Dijkstra+DFS+四value,两次Dijkstra可二刷这题理解错题意,浪费了时间
1119二叉查找树先序后序->中序二刷
1123avi树avi树构建+BFS+CBT二刷
1127后序中序->层序)可二刷常规
1131打印最短路径的格式太难了
1135红黑树判断一棵树是不是红黑树二刷里面有求树高和判断左右子树是否等高的代码
1139题目时间改了,用DFS最后一个用例超时,可以直接暴力法,终点和起点同时遍历一个结点
1143二叉查找树LCA+数据排好序就是查找树的中序遍历可二刷
1147判断堆是大根小根还是不是堆可二刷
1151LCA二刷
1155判断是大根堆小根堆还是不是堆二刷

知识点整理

知识点整理是考前临时抱佛脚看的,有两份,第一份较为详细,第二份较为精简。主要是一些样板代码和一些做题的注意点。
下面放的是详细版本的,PDF版本可以点击这里详细版本pdf。同时精简版本的PDF可以查看这里精简版本pdf

详细版知识点整理

树状数组

int getSum(int x){ 求 c[1,x]的 sum
    int sum = 0;
    for(int i = x;i >= 1;i -=lowbit(i))
    sum+=c[i];
    return sum;
}
void update(int x,int v){
    for(int i = x;i < MAXN;i+=lowbit(i))
    c[i]+=v;
}

DP(动态规划)

  1. 最大连续子序列:dp[i] = max(A[i],dp[i]+A[i]);
  2. 最长不下降子序列:dp[i] = max(1,dp[j]+1) (j=1,2,……,i-1, A[j]<=A[i])
  3. 最长回文库:
    for(int i = 0;i < n;i++){
        dp[i][i] = 1;
        if(i>0&&A[i-1]==A[i]){
        dp[i-1][i] = 1;
        ans = 2;
        }
    }
    for(int L = 3;L <= len;L++){
        for(int i = 0;i + len - 1 < len;i++){
            int j = i+len-1;
            if(dp[i+1][j-1]==1&&A[i]==A[j]){
                dp[i][j]=1;ans = L;
            }
        }
    }
    
  4. 最长公共子序列:
    dp[i][j] = {
        dp[i-1][j-1]+1; A[i]=B[j];
        max(dp[i-1][j],dp[i][j-1]);
    }
    
  5. DAG
  6. 背包问题
    void beibao(){
        for(int i = 1;i <= n;i++){
            for(int v = V;v >= w[i];v--){ 这是 01 背包问题
            for(int v = w[i];v<=V;v++){ 这是完全背包问题
                dp[v] = max(dp[v],dp[v-w[i]]+c[i]);
            }
        }
    }
    
    

  1. 最短路径

    • Dijkstra
    void Dijkstra(int s){
        fill(dis,dis+MAXN,inf);
        fill(vis,vis+MAXN,false);
        dis[s] = 0;
        for(int i = 0;i < n;i++){
            int u =-1,MIN = inf;
            for(int j= 0;j < n;j++){
                if(!vis[j]&&dis[j]<MIN){
                    u = j,MIN = dis[j];
                }
            }
            if(u==-1)return;vis[u]=true;
            for(int v = 0;v < n;v++){
                if(!vis[[v]&&G[u][v]!=inf){
                    if(G[u][v]+dis[u] < dis[v]){
                        //处理语句
                    }else if(G[u][v]+dis[u]==dis[v] && 其他条件){
                        //处理语句
                    }
                }
            }
         }
    }
    void getPath(int s){
        if(s==start){
            tempPath.push_back(s);
            1.在这里计算对应的值
            2.计算完后进行相应的更新
            3.达到条件时 path = tempPath;
            tempPath.pop_back();
        }
        tempPath.push_back(s);
        for(int i = 0;i < pre[s].size();i++){
            getPath(pre[s][i]);
        }
        tempPath.pop_back();
    }
    
    • SPFA
    bool SPFA(int s){
        memset(inq,false,sizeof(inq)); inq 记录顶点是否在队列中
        memset(num,0,sizeof(num)); num 数组记录顶点的入队次数
        fill(d,d+MAXN,INF);
        queue<int> q;
        q.push(s);
        while(!q.empty()){
            int u = q.front();q.pop();
            inq[u] = false; u 不在队列中
            for(int j = 0;j < Adj[u].size();j++){
                int v = Adj[u][j].v;
                int dis = Adj[u][j].dis;
                if(d[u]+dis < d[v]){
                    d[v] = d[u]+dis;
                    if(!inq[v]){
                        q.push(v);
                        inq[v] = true;
                        num[v]++;
                        if(num[v]>=n)return false;
                    }
                }
            }
        }
        return true;
    }
    
  2. 最小生成树(Prim算法)

int prim(){
    fill(d,d+MAXN,inf);
    d[0] = 0;
    int ans = 0;
    for(int i = 0;i < n;i++){
        int u = -1,MIN = inf;
        for(int j = 0;j < n;j++){
            if(vis[j]==false && d[j]<MIN){
                u = j;MIN = d[j];
            }
        }
        if(u==-1)return false;
        vis[u]=true;
        ans += d[u];
        for(int v= 0;v < n;v++){
            if(!vis[v]&&G[u][v]!=inf){
                if(G[u][v] < d[v]){
                    d[v] = G[u][v];
            }
            }
        }
    }
    return ans;
}
  1. 树的遍历
(1).DFS
void DFS(int s){
    vis[s] = true;
    这里是一些操作,包括剪枝之类的
    for(int v = 0;v < n;v++){
        if(!vis[v]&&G[s][v]!=inf){
            DFS(v);
        }
    }
}
void DFSTravel(){
    for(int i = 0;i < n;i++){
        if(!vis[i])DFS(i);
    }
}

(2).BFS
void BFS(int s){
    queue<int> q;
    q.push(s);inq[s]=true;
    while(!q.empty()){
        int u = q.front();q.pop();
        for(int v = 0; v< n;v++){
            //计层,用结构体的话,放在这里
            if(!vis[v]&&G[u][v]!=inf){
                q.push(v);
                inq[v] = true;
                //计层的话,开数组放在这里,否则会被覆盖
            }
        }
    }
}
void BFSTravel(){
    for(int i = 0;i < n;i++){
        if(!inq[i])BFS(i);
    }
}
  1. 图论的题
    这种题只能根据题目要求进行判断,另外判断成环,可以用 set 元素个数和读入结点个数是
    否相等来判断

  2. 拓扑排序
    较为简单,使用队列进行模拟就行,使用stl中的优先队列更方便。

栈,队列,哈希,链表

  1. 链表:数据与 next 分离,剔除无用结点,长度为 0 单独考虑
    2.栈:如果考一个序列的所有出栈情况,最笨的方法是排列后分别判断;
判断一个序列是否是出栈顺序:
bool is(){
    for(int i = 1;i <= n;i++){
        s.push(i);
        if(q.front()==i){
            while(!s.empty()&&q.front()==s.top()){
            q.pop();s.pop();
        }
    }
    if(!s.empty())return false;
    else return true;
    }
}

3.队列:容易题不常见,难的就是银行排队的题目,见机行事吧
4.hash(二次探测法)

bool insert(int x){ 二次探测法插入
    for(int i = 0;i < size;i++){
        int index = (x+i*i)%size; 这里有的把 i*i 放在外面,根据题意判断
        if(hash[index]==-1){
            hash[index] = x;
            return true;
        }
    }
    return false;
}
int search(int x){
    int cnt = 0;
    for(int i = 0;i <= size;i++){ 与插入不同,这里带等号
        int index = (x+i*i)%size;
        if(hash[index]!=x && hash[index]!=-1){
            cnt++:
        }
    }
 return cnt;
}

红黑树

判断方法:根为黑色,所有红结点的儿子都是黑节点,所有结点左右子树的(黑节点)高度相

bool judge1(int s){ 判断所有红节点的儿子都是黑节点
    if(s == -1)return true;
    if(red[s]){
        if(T[s].l!=-1&&red[T[s].l])return false;
        if(T[s].r!=-1&&red[T[s.r]])return false;
    }
    return judge1(T[s].l)&&judge1(T[s].r);
}
bool judge2(int s){ 判断所有结点左右子树的(黑结点)高度相等
    if(s == -1)return false;
    int L = getH(T[s].l);
    int R = getH(T[s].r);
    if(L!=R)return false;
    return judge2(T[s].l)&&judge2(T[s].r);
}
int getH(int s){
    if(s == -1)return 0;
    int L = getH(T[s].l);
    int R = getH(T[s].r);
    return max(L,R)+1; 这是正常求树高
    return black[s]?max(L,R)+1:max(L,R); 这是求(黑结点)高度
}
平衡二叉树

四种旋法:
* 插在左子树的左子树:树右旋
* 插在左子树的右子树:左子树左旋,然后树右旋
* 插在右子树的右子树:树左旋
* 插在右子树的左子树:右子树右旋,然后树左旋
实现代码:

int leftRotate(int s){ 左旋
    int temp = T[s].r;
    T[s].r = T[temp].l;
    T[temp].l = s;
    return temp;
}
int rightRotate(int s){ 右旋
    int temp = T[s].l;
    T[s].l = T[temp].r;
    T[temp}.r = s;
    return temp;
}
int leftRightRoate(int s){ 左右旋
    T[s].l = leftRotate(T[s].l);
    return rightRotate(s);
}
int rightLeftRotate(int s){ 右左旋
    T[s].r = rightRotate(T[s].r);
    return leftRotate(s);
}

int insertT(int& s,int k){
    if(s == -1) return newnode(k);
    else if(k < T[s].k){
        T[s].l = insertT(T[s].l,k);
        int L = getH(T[s].l),R = getH(T[s].r);
        if(L - R > 1){
            if(k < T[T[s].l].k){
                s = rightRotate(s);
            }else{
                s = leftRightRotate(s);
            }
        }
    }else{
        T[s].r = insertT(T[s].r,k);
        int L = getH(T[s].l),R = getH(T[s].r);
        if(R-L > 1){
            if(k > T[T[s].r].k)
                s = leftRotate(s);
        else
            s = rightLeftRotate(s);
        }
    }
    return s;
}

堆首先必须是一个完全二叉树。

  1. 判断是大根堆还是小根堆
void judge(){
    for(int i = 2;i <= n;i++){
        if(T[i/2]>T[i])small = false;
        if(T[i/2]<T[i])big = false;
    }
    if(!small&&!big)不是堆
    else if(small) 小根堆
    else if(big) 大根堆
}
  1. 堆排序:堆排序的特点是后面有 k 个数是排好序的且,这 k 个数都 >= 第一个数每次从后向前找到第一个 >= T[1]的数 T[p],交换 T[p]和 T[1]后执行下面的调整算法
void adjust(int low,int high){
    int i = low,j = 2*i;
    while(i <= high){
        if(j+1<=high && T[j+1]>T[j])j+=1;
        if(T[i]>=T[j])break;
        swap(T[i],T[j]);
        i = j,j = 2*i;
    }
}
完全二叉树(判断是否是完全二叉树或者建树)
  1. 建树:直接用数组模拟
  2. 判断
void bfs(int s){ 用 bfs 判断否是完全二叉树,如果遇到了非内结点后又遇到内结点则不是
    queue<int> q;
    q.push(s);
    int flag = 0;
    bool isCom = true;;
    while(!q.empty()){
        int u = q.front();q.pop();
        if(T[u].l!=-1){
            if(flag==1) isCom = false;
            q.push(T[u].l);
        }else flag = 1; 遇到叶子结点
        if(T[u].r!=-1){
            if(flag==1) isCom = false;
            q.push(T[u].r)}else flag = 1;
    }
}
bool check(){ 直接检测
    int half = len/2;
    for(int i= 1;i < half;i++){ 所有内结点
        if(T[i].l==-1)return false;
        if(T[i].r==-1)return false;
    }
    for(int i = half+1;i <= n;i++){ 所有叶子结点
        if(T[i].l!=-1)return false;
        if(T[i].r!=-1)return false;
    }
    if(T[half].l==-1&&T[half].r!=-1)return false;
    return true;
}
哈夫曼树
  1. 如果不用建树,就用优先队列:priority_queue
  2. 如果需要建树,是从下向上建,就要在结点中添加一个指向父节点的值
普通二叉树
  1. 构建
    先序+中序:
    后序+中序:
    先序+后序:如果有只有一个儿子的结点,则会有歧义
    中序+层序:这个暂时还未考
  2. LCA(与上面构建类似)
    五种情况:
    u 是根
    v 是根
    u,v 在根的左侧
    u,v 在根的右侧
    u,v 在根的两侧
  3. 遍历,先序遍历,后序遍历,中序遍历
void BFS(int s){
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front();q.pop();
        for(int i = 0;i < T[u].size();i++){
            int v = T[u][i];
            q.push(v);
        }
    }
 }
void DFS(int s){
    if(s == -1)return;
    for(int i = 0;i < T[s].size();i++){
        int v = T[s][i];
        DFS(v);
    }
}

并查集:一定要记得初始化

对于并查集的题目,在统计时要使用 findFa(x)来查找 father,因为 father[x]里面存的可能不
是祖先而是直系父亲

int findFa(int x){
    if(x == father[x])return x;
    else {
        int Fa = findFa(father[x]);
        father[x] = Fa;
        return Fa;
    }
}
int union(int x,int y){
    int Fx = findFa(x);
    int Fy = findFa(y);
    father[Fx] = Fy;
    return Fy;
}

排序

  1. 快速排序:特点:每次排完,排好的元素在最终结果对应的位置
int quick(int low,int high){
    int temp = arr[low];//基准数据
    while(low < high){
        //当队尾元素大于等于基准元素,向前挪动 high 指针
        while(low < high && arr[high] >= temp)
            high--;
        arr[low] = arr[high];
        //当队首指针小于等于基准元素,向后挪动 low 指针
        while(low < high && arr[low] <= temp)
            low++;
        arr[high] = arr[low];
    }
    //将 temp 放在该在的位置
    arr[low] = temp;
    return low;
}
  1. 堆排序:前面说了
  2. 插入排序:开始 k 个元素排好,后面的与输入序列一样

其他

  1. 数字转string
    sprintf(chs,"%d",sum,',');//先将数字存在 char 数组中
    string str = chs; //将 char 数组转为 string
  2. 对时间进行排序,可以使用 std 的字符串直接对字符串的时间进行比较。
  3. 对于中序,后序先序等互相获得的题目,递归条件可以改成通用
if(preL>preR)return;
else if(preL == preR){
    post.push_back(pre[preL]);
    return;
}
  1. 对于AVL树,注意:左旋右旋一定要写对,如果结果不对很可能是因为左旋右旋插入函数中,插入结点后对树进行调整时 要记得将旋后的返回值赋值给s,如: s = rightRotate(s)
  2. 除了只涉及完全二叉树和图,一定要建结点,不要浪费时间,有时候用到子树T[T[s].l],一定要先检查是否为-1
  3. 对于数1-n中有多少个num(1<= num <=9 )的题目,对第i位进行分离即可,即 ai = num,ai<num,ai>num;分别得出左右数的大小,然后乘在一起等操作即可。
  4. 有些数据可能是float,结果当int处理
  5. 取整:四舍五入:round向上取整:ceil向下取整:floor,强转
  6. 浮点转整形时,计算 sum 时,将 sum 全计算完再做,若每次都转再相加,精度严重丢
  7. hash_set用法:加上下面两句#include <hash_set> using namespace __gnu_cxx;
    (11). string.rbegin(),rend()是从右向左遍历
    (12). atoi(str.c_str())需要用到stdlib.h库, cctype库里有一些用到的函数
    (13). cin >> int;后面跟getlinegetline会得到一个\n

其他

在这里说一些自己当时犯的错,供后来人吸取教训:

  1. 平时做题练习的时候一定不要在意题目的对错,刚开始刷题的时候因为在意对错和得分,甚至去网上抄袭别人的代码,一知半解的。然后就得到了报应。第一次考试的时候才考79。不得已二刷,然后耽误了考研的复习。
  2. 晴神的书中涉及的知识点太多太杂,很多是以前命题的知识点,一定要把后一半的题目做完,PAT的命题风格变化还是很明显的。有些知识点已经很久没有出了,或者出在20分的题中。把精力多放在与数据结构知识点结合的方面。
  3. 对于知识点方面,可以看看这篇博客,总结的很详细。宇宙无敌PAT考纲。这个博客的PDF版本我整理下来了,可以在这里查看:考纲PDF版本。代码方面,建议看柳神的博客,所有题目的PDF版本建议去柳神博客上购买订阅,几块钱不贵。
  4. 祝看到这篇博客的同学PAT满分,考研顺利进浙大。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaXuwl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值