简介:这是一份面向计算机考研学子(尤其针对408统考与893自命题院校)的数据结构与算法题总结,共36页,内容精选自近年高频考点与LeetCode经典题目,覆盖数组、链表、栈、队列、二叉树、排序、双指针、二分查找、贪心、动态规划等核心模块。例如数组合并排序、约瑟夫环高效解法、栈实现队列、最小栈、删除链表倒数第n个节点、链表中环的入口点、二叉树前序/中序/后序与层序遍历、前序+中序构建二叉树、快速排序/堆排序/归并排序、Top K问题、最长公共子序列、01背包等,均配有思路说明与可运行的C代码实现。资源为单个PDF文档,压缩包约1.67MB,内容紧凑、排版清晰,适合打印或手机端随时翻阅。目前已有1681人学习下载,尤其适合冲刺阶段快速回顾算法模板、查漏补缺,也可作为408与893自命题备考的案头参考资料。
1. 考研数据结构算法题:36页不是拿来背的,是拿来用的
看到“考研数据结构算法题总结36页(893+408)”这个标题的人,多半已经进入了刷题中后期:选择题能做对七八成,一碰到手写算法题就卡壳。这份36页整理的本质不是背诵讲义,而是一张把考题、代码模板、复杂度浓缩在一起的作战地图。它要解决的实际问题只有一个:在408统考和自命题893类科目里,算法题的考点有限,把高频考法练到肌肉记忆,比在题海里乱撞省力得多。
这个方向适合两拨人。一是跨考或者时间紧张的考生,需要用最短的时间把最容易拿分的手写代码练熟;二是本来基础不错,只想在考前做一次系统检索的科班生,用这36页查漏补缺,而不是重新啃一遍教材。需要提醒的是,任何总结都替代不了亲手写代码,这条路线能不能走通,完全取决于你怎么“用”它。
2. 先拆408与893的算法题考法:考点清单按出题概率排
同样一道算法题,在408和893里是两种打法。很多人拿到资料就从头翻到尾,这是浪费时间。第一件事,是把两种考法的差异搞清楚,再决定复习的侧重点。考法不同,给分逻辑不同,你需要练的“写法”也就不同。
2.1 408统考大题:一题定生死,考的是最小可运行代码
408统考的数据结构部分,算法大题通常出现在试卷靠后的大题中,分值在10到15分之间。题干风格很固定,一段话加一个数据结构:“给定一个带头结点的单链表,设计算法将所有偶数位置节点移到最前面”“已知二叉树用二叉链表存储,写出求树高的非递归算法”,这类描述就是典型考法。
408阅卷是采点给分,算法思想、数据结构选型、核心代码、复杂度分析各占一块。换句话说,它不需要你写出能编译通过的程序,但必须让阅卷人肉眼看到完整的闭环:用什么数据结构、怎么处理边界、返回什么。手写环境下代码必须短,所以考点高度集中在单链表和二叉树上,数组矩阵这类内容更多出现在选择题里,图的大题则以拓扑排序、最短路径思想阐述为主,很少让你实现整棵复杂算法。
还有一点:暴力枚举思路在408的选择题里可以用来验证小规模数据,但大题必须落到正解上。阅卷人不会因为你的暴力解法能跑就给满分,它缺的是复杂度这部分的采分点。
2.2 893自命题科目:范围更窄,但“写出来”的要求更高
893不是全国统一命名的科目代码,而是不少自命题院校习惯用的专业课编号。它和408最大的区别是:数据结构单独成卷,算法设计题的分值比重更高,有时会连续出现两到三道算法大题,不再是“一道题定生死”的格局。
这种考法意味着两件事。第一,考点范围反而更集中,线性表、栈与队列、二叉树是绝对主战场,树的非递归遍历、栈的表达式求值、链表的合并与反转都是高频题;第二,每道题要求你写出完整的处理流程,从函数头到返回值,缺一步都会显得不完整。KMP算法在这种卷子里也更容易出现,因为它既是经典算法,又适合出一道“请写出模式串的next数组并说明匹配过程”的完整题目。
很多资料把KMP讲得玄学,其实在考研场景下,它就是个记忆型算法。核心不是理解失配回退的数学证明,而是能把next数组的推导稳定写在卷面上。这部分后面单独展开。
2.3 一张考点频率表:把有限时间优先投给高分值考法
拿到36页资料后,先别急着背。我一般会先按下面这张频率表把考点排序,再决定每天练什么。这张表适用于大多数408和893类试卷,你可以根据自己的目标院校做微调。
| 优先级 | 考点方向 | 典型考法 | 投入建议 |
|---|---|---|---|
| 高频 | 单链表反转、合并、删除 | 手写完整函数并处理边界 | 每天必练 |
| 高频 | 二叉树三种递归遍历与层序 | 递归与非递归互相转换 | 每周覆盖 |
| 高频 | 快排、归并、堆排序 | 手写一趟划分或归并过程 | 掌握复杂度对比 |
| 中频 | 栈与队列综合应用 | 中缀转后缀、双端队列场景 | 理解型练习 |
| 中频 | KMP的next数组 | 手写next数组并说明匹配 | 背过程加推演 |
| 中频 | 二叉搜索树插入删除 | 与中序遍历结合考 | 多写几遍 |
| 低频 | 图论 | 拓扑排序、单源最短路思想 | 会画流程能讲清 |
| 低频 | 哈希冲突处理 | 线性探测、链地址法 | 重点在选填题 |
| 低频 | 数组与矩阵压缩 | 特殊矩阵下标换算 | 选填题为主 |
排序算法在408里很少让你手写全量代码,更多是考一趟过程和第k趟结果,但893自命题会直接要求写出快排或归并的核心函数。所以“数据结构排序算法”这个方向,优先级应该排在链表和树之后,但不能完全不练。
3. 三遍法把总结页变成自己的:操作步骤与时间参数
很多人的复习路径是:把36页从头到尾划重点,划完合上,发现脑子里什么都没留下。这不是记性差,而是方法错了。我一般用三遍法处理这类浓缩资料,每一遍的编码方式不同,第一遍在组织信息,第二遍在强制提取,第三遍在模拟真实考场状态。
3.1 第一遍:把每一页改写成“题干→考点→模板”三联卡
这一步做的是信息重组。具体操作为:准备一叠A4纸或空白卡片,把36页里每一页的核心内容压缩成三行。第一行写这一页对应的题干特征,比如“带头结点的单链表、要求原地修改”;第二行写考点,比如“单链表反转、双指针”;第三行写模板,比如“precurnextNode三步循环”。
写完之后不要立刻翻下一页,而是合上资料,对着自己写的三行复述一遍完整思路。复述不出来的地方,说明这一页还没有真正进脑子,用红笔标记。这个动作在认知科学里叫“检索练习”,效果远好于反复划线。每完成一页,在页码旁边给自己打分:能直接说清思路的给5分,只能说出大概的给3分,完全懵的给1分。
第一遍的速度会有落差,有人一天只能过六到八页,这很正常。36页的资料,按每天六页的节奏需要六天左右,考虑到中间穿插练习,十天内完成第一遍是比较合理的参数。
3.2 第二遍:手写默写加边界检查,别让“看着会”骗了你
第二遍是核心,也是最容易偷懒跳过的一步。做法很简单:关上所有资料,拿出一沓白纸,把每一页对应的代码模板默写出来。写完之后再打开资料逐行对照,用红笔标出漏掉的部分。
最常见的漏写集中在边界条件上。我给自己定了一张默写检查表,每次写完后按表过一遍:
| 检查点 | 常见漏写 | 对策 |
|---|---|---|
| 链表判空 | while (cur != NULL)写成while (cur->next != NULL) | 先画图再动笔 |
| 树递归出口 | 忘写root == NULL判断 | 第一行写出口 |
| 数组下标越界 | 边界条件没想清楚就开始写 | 用前闭后开区间描述 |
| 复杂度 | 只写 T(n),忘写 S(n) | 代码末尾固定留两行 |
手写和机写的差别很大。机器上编译不过会有提示,白纸上编译不过只有你自己知道。第二遍的目的就是把“看着眼熟”变成“提笔就写”,这个过程没有捷径,默写次数是唯一的变量。
3.3 第三遍:限时模拟,按“1-3-10-2”节奏练
第三遍是把时间参数加进来。真题考场上,一道算法大题从读题到完成作答的时间窗口通常不超过15分钟。我把它拆成四个阶段:1分钟读题并圈出关键条件,3分钟画出存储结构和处理流程,10分钟写代码,最后2分钟做边界自查。
这个节奏不是随便定的。1分钟读题能训练你快速锁定考点,3分钟画图能让思路可视化,手写代码时不容易乱,10分钟的代码阶段是主战场,2分钟自查则专门检查空输入和单节点这类边界。很多人在考场上翻车不是因为不会写,而是因为写得太快,漏掉了判空条件,最后时间不够来不及改。
第三遍的操作建议是:每天限时完成两道真题,用手机倒计时。时间到了就停笔,哪怕没写完也进入复查阶段,然后根据实际用时调整下一题的策略。这样练十天左右,你能明显感觉到写题时的节奏感,而不是一上来就埋头写代码。
4. 必练的三大代码模板:链表、二叉树、KMP的落地写法
36页里你会看到很多模板,但真正需要每天默写的,我建议聚焦在三个原型上:单链表反转、二叉树层序遍历、KMP的next数组。前两个覆盖了408最常考的线性表和树,第三个覆盖了字符串算法的记忆型考点。
4.1 单链表反转:代码模板与区间反转变体
单链表反转是线性表大题的底座,合并、删除、排序都建立在指针操作能力上。下面是考研手写常用的C语言版本,节点定义用不带头结点的形式:
typedef struct node { int data; struct node *next; } LNode; LNode *reverseList(LNode *head) { LNode *prev = NULL; // 已完成反转部分的前驱 LNode *cur = head; // 当前待处理节点 while (cur != NULL) { LNode *nextNode = cur->next; // 保存后继,防止断链 cur->next = prev; // 反转当前节点指针 prev = cur; // 前驱后移 cur = nextNode; // 当前节点后移 } return prev; // 反转后新头结点 }逻辑说明:这段代码的核心是让每个节点的next指向前驱,而不是后继。保存nextNode这一步最容易漏,一旦先把cur->next = prev执行完,原来的后继节点就找不到了,链表断在后面。循环结束后prev正好停在原链表的尾节点,也就是新链表的头结点。
参数说明:函数接收的是头结点指针,不带头结点的链表直接传head即可;如果是带头结点的链表,需要跳过虚拟头结点,传入head->next。返回值同样要区分,不带头结点时返回prev,带头结点时可以用prev重新挂到虚拟头结点后面。这个细节不写清楚,阅卷人无法判断你对链表结构的理解是否到位。
搞清楚这段之后,把它变成变体练习。区间反转(反转第m到第n个节点)就是在这个模板前面加一个pre指针记住入口位置;两两交换相邻节点则相当于在反转思路上套一层循环。能把这几个变体讲清楚,线性表的大题基本稳了。
4.2 二叉树层序遍历:队列应用与双端队列之字形扩展
树的中序、先序、后序递归遍历虽然高频,但递归版本太简单,考研大题更多考非递归版本和层序遍历。层序遍历用队列实现,代码能直接迁移到“求树高”“判断完全二叉树”等题目上。
#include <stdio.h> #define MAX 100 typedef struct BTNode { char data; struct BTNode *left, *right; } BTNode; void levelOrder(BTNode *root) { if (root == NULL) return; BTNode *queue[MAX]; int front = 0, rear = 0; queue[rear++] = root; // 根节点入队 while (front < rear) { BTNode *p = queue[front++]; // 出队一个节点 printf("%c ", p->data); // 访问当前节点 if (p->left != NULL) queue[rear++] = p->left; if (p->right != NULL) queue[rear++] = p->right; } }逻辑说明:层序遍历的核心是“先进先出”。先把根节点入队,然后循环执行出队、访问、左右孩子依次入队,队列为空时遍历结束。这里用数组实现静态队列,front指向队首,rear指向队尾的下一个空闲位置,出队时front++,入队时rear++。考研卷面写这种静态队列最稳妥,不容易在动态内存上出错。
参数说明:root是二叉树根节点指针,为空时直接返回。MAX是队列容量上限,按教材习惯取100即可,实际考试中节点数不会超过这个量级。如果考卷要求写出循环队列版本,把front和rear对MAX取模即可,整体框架不变。
层序遍历的扩展点是“之字形层序遍历”,也叫锯齿形遍历。它要求奇数层从左到右、偶数层从右到左,这时普通队列不够用,需要用到双端队列。常见做法是维护两个方向相反的弹出规则,在入队孩子时交替使用头插和尾插。这个变体在408真题里出现过,建议顺手练一遍。
4.3 KMP的next数组:下标起点统一,回退不靠感觉
KMP在考研数据结构里是个特殊存在,它不像链表模板能靠画图推出,next数组的推导一旦开始用“感觉”写,基本就会乱。下面是考研常用的0起始下标版本。
#include <string.h> void getNext(char *p, int *next) { int i = 0, j = -1; int len = strlen(p); next[0] = -1; while (i < len) { if (j == -1 || p[i] == p[j]) { i++; j++; next[i] = j; } else { j = next[j]; // 回退到更短前缀 } } }逻辑说明:i是模式串的当前匹配位置,j是已匹配前缀的长度。当p[i]和p[j]相等时,前缀长度加一,写入next[i];不相等时,j回退到next[j],相当于把模式串的已匹配部分不断缩短,直到找到能衔接的位置。这就是KMP相比暴力匹配省时间的核心:主串指针不回头,模式串指针按next回退。
参数说明:p是模式串,next数组需要预先分配至少strlen(p) + 1个位置。这里必须统一下标起点,我习惯写0起始版本,并在代码旁边标注“下标从0开始,next[0] = -1”这一行。常见扣分点是混用0起始和1起始:1起始版本里next[1] = 0,判断条件也相应变成j == 0。两种写法都可以,但一张卷面上只能用一种,考前选定一个版本并固定下来。
这个模板值得每天默写一遍,因为它不能用“画面感”记忆,必须靠肌肉记忆。默写时顺手把“为什么回退到next[j]”用一句话写在旁边,防止考场上只记得代码、说不清思想。
5. 算法大题常见翻车避坑:五个高频扣分点与对策
手写算法题和机写算法题是两种完全不同的考试形式,很多失分点不在算法本身,而在答题习惯上。下面这五条是我见过最多的翻车场景,每一条都按现象、原因、解决的顺序说清楚。
5.1 只背代码不背变体:换一个条件就原形毕露
现象:链表反转模板背得很熟,一遇到“反转链表第m到第n个节点”就卡住,不知道从哪里下手。
原因:背模板时只记住了指针移动的几步,没有理解循环里“摘下一个节点挂到新头部”这个动作的本质。模板是死的,但考题会在条件上做变化。
解决:把模板拆成思想来记。链表反转的循环体本质是“把当前节点摘出来,放到已反转部分的头部”,区间反转只是多了一个pre指针,先走到第m个位置的前驱,再对m到n这段执行同样的摘挂动作。做题时先问自己:变在哪里?原模板哪些代码还能用,哪些需要加指针。
5.2 边界条件漏写:空链表、单节点、尾指针
现象:逻辑主线写对了,但while条件用了cur->next != NULL,导致空链表直接解引用崩溃;或者树递归忘了写root == NULL判断,整个函数在空树上直接出错。
原因:手写时没有编译器报错,脑内模拟往往只跑了正常长度的一条路径,空输入、单节点这些异常分支被自动带过。
解决:每次写完代码后,按固定顺序自查三遍:空输入、单节点、正常长度。链表题额外检查尾节点处理,树题额外检查根节点为空。我把这个动作写在自己的草稿纸顶部,每次模拟练习都能看到。
5.3 复杂度分析不写或写得含糊:丢的全是采分点
现象:代码写完,时间复杂度写了O(n),空间复杂度却没写;或者写成“O(n)级别”“线性复杂度”这种模糊说法。
原因:练题时只关注算法本身,忽略了考研阅卷是按采分点给分,复杂度分析就是其中一项。
解决:在每道题代码的最后固定写两行,格式统一为“T(n)=O(n),S(n)=O(1),n为链表长度”。这样既格式清晰,又让阅卷人一眼看到你的分析能力。空间复杂度哪怕只写O(1),也比空着强。
5.4 刷题分配失衡:把高频题型的正确率先稳住
现象:花大量时间啃图论难题和复杂排序变体,回头发现单链表反转已经写得磕磕绊绊,基础题反而没拿到分。
原因:越难越容易让人产生“我练了就是提升”的错觉,但考研不是算法竞赛,难度上限就在那里,高频考点才是决定分数的大头。
解决:按第2章的频率表分配精力,高频题型至少占据70%的练习时间。图论和复杂动态规划可以作为查漏补缺,但不能挤占链表和树的位置。考前两周如果时间不够,优先级排序应该是:链表、二叉树、排序、KMP,后面三项可以战略性放弃深挖。
5.5 “假会”陷阱:看懂的题和能默写的题是两回事
现象:翻开答案觉得每一步都合理,关上答案自己写就卡壳,甚至第一步就不知道怎么开头。
原因:看答案是“识别型记忆”,自己写是“生成型记忆”,前者的强度远低于后者。刷题量不等于掌握度,默写才是检验标准。
解决:看完每道题后,必须合上资料当场默写一遍,第二天再默写一次,周末找一道相似变体独立完成。三次默写都通过,这道题才算真会了。这也是第三遍限时模拟的意义所在。
6. 把36页变成真本事的验证方法:给自己做一次考前压力测试
复习到最后阶段,最怕的就是“自我感觉良好”。我对自己的验证方式只有一个:合上所有资料,拿出真题,像上考场一样写一遍。模拟环境和真实考场越接近,暴露的问题越真实。
具体步骤如下:选一套真题,只留空白答题纸和笔,手机开倒计时,不查任何资料,按“1-3-10-2”的节奏完成一道算法大题。写完不急着对答案,先按采点评分标准给自己打分:算法思想是否清晰,数据结构选型是否合理,边界条件是否齐全,复杂度是否写完整。每个环节扣多少分都标注出来。把弱项写在这道题旁边,第二天用同类题目专项补。
如果不想用真题,也可以用五个自问自查来验证:能否不看笔记写出单链表反转并在两分钟内标出所有边界判断;能否说清层序遍历为什么要用队列而不是栈;能否独立写出KMP的next数组并用一句话解释回退逻辑;能否写出归并排序的递归框架并答出它的空间复杂度;能否在10分钟内完成一棵二叉搜索树的节点插入实现。五个问题都能秒答,说明这36页你是真吸收了一部分。
这个验证方法是我当年吃亏换来的。考前一周我才发现,自己看着答案能说清链表反转,合上答案就断档。后来连续三天,每天早上第一件事就是合书默写这个模板,考场上那道题才没有崩。记住:36页是索引,手是生产力,顺序别搞反。希望帮到你。
本文还有配套的精品资源,点击获取