简介:面向893自命题与408统考考生的数据结构算法题总结,共36页,将考研高频题按数据结构与算法模块系统分类。内容覆盖数组、链表、栈、队列、二叉树等核心结构,涉及合并排序数组、约瑟夫环、栈实现队列、最小栈、逆波兰表达式求值、设计循环队列、链表环入口与反转、删除重复元素、二叉树前中后序及层序遍历、有序数组转二叉搜索树等经典代码题;算法部分则包括插入、冒泡、选择、希尔、快排、堆排、归并、基数、计数、双轴快排等排序方法,以及双指针、二分查找、DFS、贪心、动态规划等高频解题思路,部分题目也保留了LeetCode风格,便于对接刷题习惯。资源共1个PDF文件,仅1.67MB,轻巧易携带,支持打印或电子设备阅读。已有1684人浏览学习,是考研人常用的浓缩复习资料。每一道题均配有可直接运行的C语言代码与要点注释,既能帮助读者快速掌握算法模板,也有助于在考场中稳定输出规范解法,适合冲刺期集中突破。 “那 36 页总结抄了三遍,考场上遇到变体题还是卡壳”——这是我见过最多的复习翻车姿势。原因很简单:把总结类资料当成了背诵手册。893 是部分院校自主命题的专业课代码,408 是统考计算机学科专业基础综合,不管考哪种,数据结构算法题都是丢分重灾区,所以市面上才会出现“36 页高频题总结”这类资料。它真正的价值不是让你背,而是把高频考点和可复用模板集中到一个极小的信息密度里。这篇笔记就讲怎么把这份总结变成自己的代码能力:拆考点、三轮手撕、避坑清单、答题策略,最后压成考前自检卡。
2. 拆考点:数据结构算法题的高频题型与两类考试的侧重差异
2.1 893 与 408 的考查差异,先落在题量、分值和判卷上
先说 408。408 统考总分 150 分,分布在数据结构、计算机组成原理、操作系统、计算机网络四门课里。数据结构部分通常占 45 分左右:选择题约 14 道,大题一般 2 道,其中大概率有一道算法设计题。因为整张卷子题量很大,留给算法题的思考时间极其有限,这就要求看到题干的一瞬间能立刻判断“这题考什么模板、用什么数据结构”,而不是现场推演。
893 是各校自主命题,情况更杂。常见的一种形态是整张卷子 150 分全部落在数据结构上,题型可能是填空、选择、简答加两三道算法设计题;另一种形态是数据结构与操作系统合卷,但算法题仍然占据核心分值。相比 408,893 的算法题单题分值更高,动辄 15 到 25 分,判卷也更看重最终代码的完整度。同一个模板,在 408 里可能只要写出关键循环就能拿大半分,在 893 里却要求函数签名、边界处理、返回值设计全部到位。
所以在使用这 36 页之前,先问自己考的是哪一类。考 408,重点练“快速定位模板 + 按步骤写出可读代码”;考 893,重点练“完整函数输出 + 复杂度分析 + 边界条件”。两者对同一道题的要求高度不同,泛泛地刷题大概率两头都抓不牢。
判卷风格差异也直接影响答题姿势。408 是按步骤给分,思路注释、数据结构定义、核心代码分别对应得分点,代码写不完时前面的描述能捞回不少分。893 因为是自命题,更看重结果正确性和代码的“完成感”,很多时候宁可代码朴素一点,也要保证结构闭环。这也是为什么同样刷完 36 页,有人分数差距很大的原因之一。
2.2 值得逐行默写的 8 类模板:从线性表到图论
拿到 36 页后,先别急着从头看到尾。我一般会先把里面出现的题型归拢成下面几类,确认覆盖度。这 8 类基本对应统考和自命题最高频的出题区间:
| 题型类别 | 典型题目 | 首选数据结构 | 必背原因 |
|---|---|---|---|
| 线性表合并/逆置 | 两个有序链表合并、链表逆置 | 单链表、头尾指针 | 指针操作的基本功 |
| 栈应用 | 括号匹配、表达式求值 | 栈、数组模拟栈 | 栈的天然场景 |
| 树的遍历 | 非递归前/中/后序 | 栈、队列 | 递归转迭代是常考点 |
| 层次遍历 | 按层输出二叉树结点 | 队列 | 图 BFS 的前置技能 |
| 图遍历 | 连通分量计数、路径是否存在 | 邻接矩阵/邻接表 | DFS/BFS 两种框架 |
| 最短路径 | Dijkstra、Floyd 手写 | 邻接矩阵 | 复杂度分析必考 |
| 查找 | 二分查找、哈希表构造 | 数组、哈希表 | 边界条件极其经典 |
| 排序 | 快排、归并、堆排手写 | 数组 | 年年都有学校直接考 |
每一类在 36 页里对应的往往不止一道题,而是一组题。整理时我会在每页顶部写一句“入口判词”,比如“看到无序数组求第 K 大,立刻想到快排的 partition”。这句话在考场上比整段代码更有用,因为考场第一步是定位,不是默写。
以“二叉树非递归中序遍历”为例,这是 408 和 893 都爱考的模板。常见做法是手写一个数组模拟栈,不用系统栈,也不依赖任何库:
// 二叉树非递归中序遍历:左链入栈,出栈访问,再转向右子树 void InOrder(BTNode *root) { BTNode *stack[128]; // 笔试用数组模拟栈,容量按题目结点数上限调整 int top = -1; BTNode *p = root; while (p != NULL || top != -1) { // p 不空或栈不空,两个条件缺一不可 if (p != NULL) { // 一路向左压栈 stack[++top] = p; p = p->left; } else { // 左子树走到尽头 p = stack[top--]; // 弹栈 visit(p); // 在这里打印或计数 p = p->right; // 转向右子树,继续循环 } } }这段代码的逻辑核心是“模拟递归栈的进出时机”。p != NULL负责把根节点一路向左压栈,等到p为空时弹栈访问,再让p指向右子树。很多人栽在把p = p->right写在别的分支,导致右子树被跳过或者死循环。栈容量 128 是假设结点数不超过这个值,考场上我会直接写宏定义#define MAXSIZE 128,让阅卷人觉得你有模块化意识。
时间空间复杂度也要顺手写在代码末尾:时间复杂度 O(n),空间复杂度 O(h),h 是树高。这个细节在 893 里往往是压轴小问,在 408 里也常作为第二小问出现。前序遍历只需要把访问时机移动到入栈前;后序遍历更绕,需要标记第二次访问,属于进阶内容,36 页里一般也会单列,值得单独手撕两遍。
3. 把 36 页总结变成自己的:三轮精读与手撕流程
3.1 第一轮标注:把每一页的题目拆成“会 / 半会 / 不会”
拿到 36 页,第一轮绝对不是从头看到尾,而是做状态标注。我会按照“线性表 → 栈队列 → 树 → 图 → 查找排序”的顺序,每天处理 3 到 5 页,对每一页里的模板题做三态判断:
- 会:合上答案能完整默写,包括边界处理和复杂度;
- 半会:翻开答案能看懂,合上写不出来;
- 不会:连思路都陌生,需要先回教材补原理。
这个动作很费时间,但值得做。因为 36 页的问题在于信息密度太高,从头到尾看一遍,你分不清哪些是已经掌握的、哪些是真薄弱。标注之后,后续每一轮的时间投入顺序就清楚了:半会题优先,因为投入产出比最高;不会题不要直接在总结页上硬啃,先回去看教材对应章节,否则硬背下来的也是黑匣子。
我一般会在每页的边角写一个简短的追踪表,不需要单独建文档:
| 页码 | 考点 | 状态 | 后续计划 |
|---|---|---|---|
| 第 03 页 | 有序链表合并 | 半会 | 第二轮优先手撕 |
| 第 14 页 | 中序非递归遍历 | 会 | 每周复写一次 |
| 第 28 页 | 平衡二叉树旋转 | 不会 | 先补教材再回看 |
第一轮的理想时长是 4 到 5 天,不是让你把每道题都写一遍,而是完成“定位“。这个过程结束之后,你手里应该有这样三个列表:高危模板清单、半会模板清单、已经掌握的模板清单。后续所有时间分配都以这三张清单为准。
3.2 第二轮手撕:先盖住答案,按迭代法把代码写完整
第二轮的唯一任务是把“看懂”变成“能默写”。操作方法是迭代手撕,不是对着答案抄。我常用的流程是:第一步,盖住答案,只看题干,凭记忆写函数签名和核心循环;第二步,写完后和 36 页上的参考实现逐行对比,用红笔标出差异;第三步,把标出的差异自己复述一遍,说清楚为什么参考写法更好;第四步,隔 24 小时重写一次,再过 48 小时再重写一次。
这里的关键是“隔 48 小时重写”。一天之内连写三遍,脑子记住的是短期动作记忆;隔两天还能完整写出来,才说明这个模板进入了长期记忆。每天不求多,两到三道即可,但必须是完整函数输出,不要只写核心片段。以“两个有序链表合并”为例,手撕的标准形态长这样:
// 带头结点的有序单链表合并,结果仍递增有序 LinkNode *MergeList(LinkNode *La, LinkNode *Lb) { LinkNode *pa = La->next, *pb = Lb->next; // 跳过两个头结点 LinkNode *tail = La; // tail 指向新链表的尾节点 while (pa != NULL && pb != NULL) { if (pa->data <= pb->data) { // 小者接入尾部 tail->next = pa; tail = pa; pa = pa->next; } else { tail->next = pb; tail = pb; pb = pb->next; } } tail->next = (pa != NULL) ? pa : pb; // 拼接剩余部分 free(Lb); // 释放另一个头结点 return La; // 返回合并后的头结点 }这段代码有几个手撕时特别容易被跳过的点:一是La被复用为结果链表的头结点,省掉了额外开内存;二是最后要free(Lb),很多人在投行里忘记释放另一个头结点,这在笔试里是明确的扣分点;三是返回值要写清楚。手撕的时候,我要求自己先写完边界和注释骨架,再填主体逻辑。这样即使考场时间不够,至少前面部分已经拿到了结构分。
第二轮的手撕顺序也有讲究:优先撕半会清单里的题,因为这类题离“会”只差临门一脚,最容易在短时间见效。不会清单里的题,先回教材补原理,再单独安排时间,不要混在一起撕,否则挫败感会把人劝退。每道题三次重写全部通过后,我会在追踪表上把状态改成“会”。
3.3 第三轮限时:按考试工况做变体训练
第三轮解决的问题是“会写”到“在考场压力下能写出来”。前面两轮不强调时间,因为重点是形成正确的代码结构;第三轮开始,每一道题都要限时。常见做法是中等难度题 15 分钟,压轴难度 25 分钟,具体时长根据你的目标院校题目量调整。超时没写出来的,标记为高危,回到第二轮流程重新安排重写。
同时要做变体训练,因为 36 页里每一类题给的是基准形态,考场上往往会改条件。链表的题型,把“带头结点”改成“不带头结点”,把“有序”改成“无序”;树的题型,把“输出结点值”改成“统计结点个数”,把“递归”改成“非递归”;图的题型,把邻接矩阵改成邻接表,把“求路径数量”改成“求最短路径长度”。变体不改核心思想,改的是入口判词和边界条件。
第三轮的产出是一个精简错题记录表,每个条目不超过三行:题目特征、错在哪一步、修正后的判词。比如“链表逆置:带头结点时用头插法,不带头结点时需要返回新头”,这就是一行。这个记录表会和 36 页一起成为冲刺期的核心材料。做完第三轮之后,一个模板才算真正属于你。
4. 避坑:考研算法题复习中常见的 6 个翻车现场
4.1 刷了 500 道题,考场上却写不出完整代码
现象:复习记录里题目数量不少,但考试时面对一道熟悉的题型,写到一半断掉,变量初始化、循环边界、返回值总有地方遗漏。
原因:平时刷题习惯了在草稿纸上写“核心片段”,函数签名、空指针判断、循环完整格式都被省略了。脑内有流程不等于手上有输出,考场上是逐字手写,省略过的部分全部变成扣分点。
解决:从第二轮开始,任何题目都按完整函数输出,函数头、参数、边界、主循环、返回值一个不少。少刷十道题,但每一道都完整写完,效果远好于刷五十道半成品。
4.2 模板背成“黑匣子”,题干一变就卡壳
现象:能默写快排,但题目换成“求无序数组第 K 小”,完全反应不过来该用快排的 partition 去解。
原因:背的是代码形状,没记入口条件。到底“什么特征出现时用它”,这个判断没有沉淀下来,代码就成了只能原样复现不能迁移的黑匣子。
解决:给 36 页里每个模板写一句入口判词,贴在标题旁边。快排判词就是“无序数组求第 K 大/第 K 小,使用 partition,平均 O(n)”。做题时先判词匹配,再动笔写代码。
4.3 判卷按关键步骤给分,核心代码缺失等于白写
现象:考试时间不够,大题只写了算法思想,主体代码空缺。408 卷面还能靠思路描述捞回一点分,893 直接扣到只剩一两分。
原因:答题顺序没规划,在选择题和小题上耗掉了太多时间,大题开局就陷入时间恐慌。
解决:每次模拟考必须固定答题顺序。选择题设置死线,超过时限立刻跳过;大题先花一两分钟写清算法思想、数据结构、复杂度,再写代码。这样即使代码写不完,前面三行也是有效得分点。
4.4 复杂度分析被一笔带过,压轴题白丢分
现象:代码写对了,时间空间复杂度漏写,或者写错。比如非递归遍历的空间复杂度想当然写成 O(n),实际栈深最多等于树高 O(h)。
原因:平时练习没有把复杂度当作答案的必备组成部分。考场上时间一紧,第一个被省掉的就是它。
解决:把复杂度写进每个模板的固定位置。第二轮手撕开始,代码末尾强制写一行注释:/* 时间 O(n) 空间 O(h) */。写习惯了,大纲就不会丢这一分。
4.5 边界条件读题漏读,头结点和空指针处理反向
现象:链表题题干写了“带头结点”,代码却按不带头结点写,结果整个指针操作全错;或者读题时没注意“空表返回”的要求,代码直接解引用空指针。
原因:读题时漏掉了关键限定词,平时做变体训练也没有把条件切换当成重点。
解决:训练时强迫自己读题先划关键词,带头结点、不带头结点、有序、无序、空表怎么办,全部标注出来。写代码第一行先写边界检查,再写主逻辑。
4.6 选择题时间黑洞,压轴题总差十分钟
现象:408 的选择题越做越兴奋,揪着一个有歧义的题反复推敲,最后大题只剩下不到二十分钟,算法题草草收场。
原因:没有给选择题设置硬性时间。选择题属于单题低分值,纠结五分钟不如把时间留给大题。
解决:模拟考时给选择题设定总时间上限,到点未完成就快速蒙一个并标记题号,等所有大题完成后再回头检查。这个习惯要在平时模拟中就演练,不能指望考场上自然产生时间管控能力。
5. 从 36 页到考卷:答题顺序、卷面布局与两种试卷的实战选择
5.1 408 的算法大题:先写思路注释,再动手写代码
408 的算法大题是按步骤给分的,阅卷时会看你“思路是否合理、结构是否完整、关键代码是否存在”。所以答题顺序不能是“打开就写代码”,而应该自上而下布局。
先写一行算法思想,用一句话讲清策略,比如“使用快排的 partition,把数组划分为左侧小于 pivot、右侧大于 pivot,再判断 pivot 位置是否等于 K”。然后写数据结构定义和复杂度,再开始写主体代码。这样做有两个好处:第一,即使时间不够,前面几行已经拿到了思路分;第二,写代码时思路已经明确,不容易写到一半发现策略选错。
我在第三轮限时训练时,会刻意用这种卷面布局来写答案,而不是像平时手撕一样直接甩代码。两者的差别在于:平时手撕是为自己写,训练纠错能力;模拟卷面是为阅卷人写,训练得分结构。
5.2 893 的自命题压轴:函数签名、边界检查与返回值设计
893 的自命题卷子往往没有选择题护体,算法题就是硬碰硬。阅卷人面对一整页代码,第一眼看的就是“完整度”:函数签名像不像一个能被调用的函数,边界检查有没有,返回值设计是否合理。
常见坑是“返回结果”和“通过参数带出结果”的混淆。题干说“返回新链表的头指针”,你就不需要额外用二级指针改写原头;题干说“要求原地修改”,你就不要开新结构。考试时先把题干里关于返回值的描述圈出来,再定函数签名。另一个容易翻车的是图的存储。笔试环境下邻接矩阵比邻接表稳,因为结构体声明短、不容易写错;邻接表要写边结点、顶点结点两层结构,万一忘了初始化就整道题报废。除非题干明确要求邻接表,否则考场优先写邻接矩阵。
最终检查时按三个维度自查:所有可能为空的输入是否判空、所有循环边界是否覆盖首尾、返回类型是否和函数签名一致。这一套流程在平时就要固化,考试时靠肌肉记忆执行,不靠临场思考。
5.3 考前一晚的回扫路线:用 36 页做结构化过电影
考前那一晚不适合再刷新题,也不适合把 36 页从头默写一遍。我会按目录做“结构化过电影”:摊开 36 页,对每个模板回忆四件事——入口判词、函数签名、复杂度、易错点。只用嘴说或者在脑内跑代码,不动笔。
这个过程每页控制在 1 到 2 分钟,36 页大概 60 到 90 分钟。重点看第二轮标记的半会条目和高危条目,已经掌握的快速扫过。扫完后合上资料,从头到尾按章节顺序回忆一遍题型分布,能回忆起章节名和对应的首选方法,这一晚的复习就是成功的。
考前回扫最容易犯的错是“看着答案觉得都会了”,结果第二天拿到卷子发现大脑一片空白。所以这一晚的默认姿态是合上答案复述,而不是打开答案确认。
6. 一个压箱底技巧:把 36 页压成 3 页自检卡,考前三天只翻卡
考前三天,把 36 页压缩成 3 页自检卡,这是我用着最顺手的冲刺方法。卡片的格式极其统一,每类题只占一行:
| 题目特征 | 首选方法 | 关键点 | 复杂度 |
|---|---|---|---|
| 无序数组求第 K 大 | 快排 partition | 判断分区位置 | 平均 O(n) |
| 二叉树按层输出 | 队列 BFS | 记录当前层末尾节点 | O(n) |
| 两个有序表合并去重 | 双指针 | 跳过重复元素 | O(n+m) |
卡 1 放线性表、栈、队列;卡 2 放树和图;卡 3 放查找和排序。制作过程本身就是一次高强度的知识压缩:你写下“快排 partition”六个字时,脑子里必须自动展开整个函数的结构,而不是盯着代码抄。这个压缩动作能暴露哪些模板还没真正内化——写不出来要展开的代码,说明这个模板还得回第二轮。
考前三天的用法是每天翻三遍:早上泛读全文,让所有判词过一遍;中午只看高危条目,重点回忆代码结构;晚上脑内补完代码,即对着卡片上的一行,在脑子里把整段代码运行一遍,直到输出结果为止。考前一小时只看卡片最后一遍,考场拿到试卷后的第一反应应该是“这题对应卡里的哪一行”,而不是“这题好像在哪见过”。
这个习惯是我在一次模考翻车之后才养成的。那一次我考前两天还在刷新题,拿到卷子后发现大脑里全是一团模糊的代码碎片,关键时刻调取不出任何完整模板。后来改成“考前三天只翻卡”,状态反而稳定得多。如果你现在还在为冲刺期不知道看什么发愁,建议今晚就把自己的 36 页压成三页,花不了两个小时,但考前一天你会感谢这份卡片。希望帮到你。
本文还有配套的精品资源,点击获取