
我当年备考408的时候有个特别深的感受代码题最怕的不是不会写而是到了考场上手一抖边界条件写错循环少个等号或者链表指针串了整道题十几分直接没了一大半。后来我把数据结构所有高频考点的代码按模板方式整理了一遍反复默写、反复手撕才真正解决了“看得懂、写不对”的老毛病。今天这篇东西就是想把那套整理思路完完整整分享出来。它不是简单的代码合集而是告诉你为什么算法模板能提分、怎么按章节体系化整理模板、每个模板背后的边界条件和复杂度逻辑是什么以及最后阶段怎么靠真题把模板变成肌肉记忆。无论你是刚开始复习数据结构还是已经进入冲刺阶段这篇内容都能直接拿去用。1. 先想清楚408代码题到底在考什么算法模板起什么作用很多人一上来就抱着严蔚敏的教材从头啃到尾或者是把王道单科书过了一遍又一遍代码题还是没底。原因很简单——408的代码题考的不是“会不会写某个算法”而是三个层面的东西第一你能不能读懂题目给的存储结构第二你能不能在一个小时内从题目场景里识别出它本质上在考哪个算法第三你写的代码是不是简洁、健壮、复杂度达标。这三个层面里算法模板解决的恰恰是第二和第三层。第一种是理解力靠刷题积累第二种是识别力靠总结归纳第三种是执行力靠默写模板。模板之于408代码题有点像公式之于数学你不可能在考场上去推导快排的partition怎么写你必须在平时就已经把边界条件焊死在脑子里。那什么叫“算法模板”用我自己的话说它是一段自带输入输出假设、存储结构定义、核心逻辑步骤、边界条件处理、复杂度说明的标准代码块。它不是为了应付OJ的AC而是为了让你在考场上能“带着答案进场”看到题目就能匹配对应的模板然后根据题目细节去微调。我见过不少同学整理模板的方式就是去网上抄一份代码打印出来贴在本子上回头翻都不翻。这种东西不叫模板叫收藏。真正有用的模板必须满足三个条件一是你亲手敲过、亲手推导过二是你知道它为什么这么写尤其是每个if边界条件的来源三是你在不同真题里用过它至少三五次。缺一个考场上都不敢用。还有一点需要提前说清楚408允许的代码实现语言是C或者C。虽然你平时可能用Java或者Python刷题但在408考场上规规矩矩用C/C写是最稳妥的因为阅卷标准和教材版本都以C语言描述为主。下面的模板我都会用C/C风格的伪码加真实代码呈现存储结构定义贴近严蔚敏教材和王道书的习惯。2. 按章节体系化整理模板从线性表到图的重难点拆解把408数据结构大纲过一遍代码题最常见的分布区间是线性表顺序表和链表出题频率最高二叉树和树次之图、查找、排序也会轮流出现。整理模板的时候不是每小节都要整理而是按照“手写代码风险”来筛——哪些代码你临时推大概率会写错哪些是你看到题目能现写的我的标准很简单凡是涉及指针操作、递归出口、循环边界这三种情况的全都要整理成模板凡是逻辑一步到位、几乎没有边界坑的比如简单的顺序表遍历求最大值那就没必要模板化看得懂题目就能写。下面按章节过一遍每一类模板我会说明它适用的题目场景、核心代码、必须背下的边界条件以及常见的考场失分点。2.1 线性表与顺序表模板下标语义与边界顺序表在408里最常见的出题方式是给一个顺序存储的线性表让你完成删除、插入、逆置、合并或者某些基于有序性的操作。顺序表的代码本身不复杂但它是考场上最容易因为“下标从0开始”而出错的板块。我给你一个最常用的顺序表操作模板适用范围包括“删除所有值为x的元素”“删除有序表中重复元素”“将两个有序表合并成一个有序表”这些题目。#define MaxSize 50 typedef struct { int data[MaxSize]; int length; } SqList;删除顺序表中所有值等于x的元素O(n)时间、O(1)空间的标准写法void delAllX(SqList L, int x) { int k 0; for(int i 0; i L.length; i) { if(L.data[i] ! x) { L.data[k] L.data[i]; } } L.length k; }这个模板的思想叫“双指针同向扫描”一个指针i负责遍历原表一个指针k负责记录保留元素的位置。它的巧妙之处在于不需要额外开数组原地就能完成删除。为什么这个模板值得背因为408很多题都能套它的壳。比如“删除有序表中重复元素”做法就是在上面基础上加一个判断只有当当前元素和前一个已保留元素不同时才保留。再比如“将顺序表逆置”本质上也是双指针只是指针从两端向中间走。注意所有顺序表模板默认下标从0开始。遇到题目说“下标从1开始”或者“元素从第1个开始编号”时务必在答题纸上先写上“假设线性表从0开始编号若题目未说明则以常见实现为准”之类的说明避免考官误解。顺序表另一个高频模板是二路归并。408爱考的是“两个递增有序顺序表合并成一个递增有序顺序表”bool mergeSq(SqList A, SqList B, SqList C) { if(A.length B.length MaxSize) return false; int i 0, j 0, k 0; while(i A.length j B.length) { if(A.data[i] B.data[j]) { C.data[k] A.data[i]; } else { C.data[k] B.data[j]; } } while(i A.length) C.data[k] A.data[i]; while(j B.length) C.data[k] B.data[j]; C.length k; return true; }这段代码看起来简单但考场上常犯的错是两个一是while循环里写成了i A.length导致数组越界二是最后忘了把C.length k赋值导致结果表长度未知。模板的意义就在这里把每一步都固化下来保证你在紧张状态下也不会少写关键语句。2.2 链表模板指针操作与虚拟头结点链表是408代码题的重灾区。因为链表题思路一般不难难的是把思路转化为指针操作时不丢节点、不空指针、不断链。我的链表模板体系围绕“虚拟头结点”展开它能统一解决头结点被修改、链表为空、插入删除位置为首元结点这三类麻烦。先看链表定义408真题习惯用带头结点的单链表typedef struct LNode { int data; struct LNode *next; } LNode, *LinkList;单链表逆置这个模板出现频率极高能直接解决“反转链表”“从尾到头输出链表”“判断回文链表”等变形题void reverseList(LinkList L) { if(L NULL || L-next NULL) return; LNode *pre NULL; LNode *cur L-next; while(cur ! NULL) { LNode *next cur-next; cur-next pre; pre cur; cur next; } L-next pre; }这段代码的核心是“三指针翻转法”pre指向前一个节点cur指向当前节点next临时保存下一个节点地址防止断链后找不到后续节点。三指针结构是链表反转类问题的基础模型比头插法重新建链更节省时间。链表删除值为x的所有节点的模板采用“双指针前驱记录”void delAllX(LinkList L, int x) { if(L NULL) return; LNode *pre L; LNode *cur L-next; while(cur ! NULL) { if(cur-data x) { pre-next cur-next; free(cur); cur pre-next; } else { pre cur; cur cur-next; } } }这个模板的易错点在于删除节点时pre不需要移动因为cur已经被更新到新的待检测节点只有在不删除时pre才跟着cur走。很多同学第一次写的时候无论如何都会在else分支里忘了pre前进导致后续节点全部判断失误。虚拟头结点在408笔试里不太需要真的malloc一个节点因为题目多数默认“带头结点”链表L本身就是头结点。所以模板里我把头结点默认为Lpre初始化为L而不是NULL。如果是“不带头结点”的链表所有模板在初始化处要改为pre NULL并单独处理首元结点。链表的合并、分解、找中间节点、判断是否有环也都是高频考点但核心模板和上面两个一脉相承。找中间节点用“快慢指针”快指针每次走两步慢指针每次走一步快指针到尾部时慢指针恰好在中间判断有环也是快慢指针如果相遇就说明有环。快慢指针这个模型建议单独整理成一个小模板因为它不像反转那么直观但考场上一眼看到“中间”“环”“倒数第k个”就能联想到。2.3 栈、队列与循环队列模板栈和队列在408里直接考完整代码的机会没有链表多但它的存在感体现在两个地方一是辅助其他算法比如树遍历的非递归写法、图的DFS/BFS二是循环队列的判空判满条件容易让人掉坑。顺序栈模板数据结构定义和基本操作压缩成一个快查表#define MaxSize 50 typedef struct { int data[MaxSize]; int top; } SqStack; void initStack(SqStack s) { s.top -1; } bool isStackEmpty(SqStack s) { return s.top -1; } bool pushS(SqStack s, int x) { if(s.top MaxSize - 1) return false; s.data[s.top] x; return true; } bool popS(SqStack s, int x) { if(s.top -1) return false; x s.data[s.top--]; return true; }顺序栈的核心就是top的初始化值是-1还是0这会改变入栈出栈的写法。我统一用top -1入栈先加后存出栈先取后减。这个约定在全文中保持一致就不容易混。循环队列模板重点在于“牺牲一个存储单元来判断队满”#define MaxSize 50 typedef struct { int data[MaxSize]; int front, rear; } SqQueue; void initQueue(SqQueue q) { q.front q.rear 0; } bool isQueueEmpty(SqQueue q) { return q.front q.rear; } bool isQueueFull(SqQueue q) { return (q.rear 1) % MaxSize q.front; } bool enQueue(SqQueue q, int x) { if(isQueueFull(q)) return false; q.data[q.rear] x; q.rear (q.rear 1) % MaxSize; return true; } bool deQueue(SqQueue q, int x) { if(isQueueEmpty(q)) return false; x q.data[q.front]; q.front (q.front 1) % MaxSize; return true; }循环队列为什么牺牲一个空间因为如果不牺牲队空和队满都满足front rear就分不清了。除了“牺牲空间法”408也偶尔会提到“增设tag标记法”和“计数器法”但我个人建议统一用牺牲空间法因为它是王道和严蔚敏教材的主流写法阅卷时不会有争议。2.4 二叉树遍历与递归模板二叉树的代码题核心就是遍历。先序、中序、后序、层序这四种遍历的递归写法是基础中的基础必须滚瓜烂熟。在此基础上几乎所有二叉树题目都是“在遍历过程中加条件判断”的变形。二叉树节点定义typedef struct BiTNode { int data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree;先序遍历递归模板这套模板可以扩展到求叶子节点数、查找值为x的节点、计算高度等void preOrder(BiTree T) { if(T NULL) return; // 访问根节点 printf(%d , T-data); preOrder(T-lchild); preOrder(T-rchild); }求二叉树高度的模板int getHeight(BiTree T) { if(T NULL) return 0; int leftH getHeight(T-lchild); int rightH getHeight(T-rchild); return (leftH rightH ? leftH : rightH) 1; }这个模板的递归逻辑是“自底向上归纳”空树高度为0非空树的高度等于左子树和右子树高度的较大值加1。很多人一开始会困惑为什么要选较大值因为树的高度定义就是从根到叶子最长路径上的节点数取最大值是定义的自然结果。层序遍历模板使用了链式队列这里体现出上面循环队列的辅助价值void levelOrder(BiTree T) { if(T NULL) return; SqQueue q; initQueue(q); enQueue(q, T); // 注意队列元素类型应该是BiTree这里仅示意 while(!isQueueEmpty(q)) { BiTree p; deQueue(q); // p 出队节点 printf(%d , p-data); if(p-lchild ! NULL) enQueue(p-lchild); if(p-rchild ! NULL) enQueue(p-rchild); } }注意上面层序代码里的队列是SqQueue如果队列定义是int数组存储需要把队列元素类型改成BiTree也就是typedef struct { BiTree data[MaxSize]; int front, rear; } SqQueue;。考试时定义一种“万能队列”元素类型是很常见的操作不要在这上面纠结。二叉树这部分我强烈建议整理一张“遍历变形映射表”比如求节点个数任意遍历计数器求叶子节点个数先序/中序/后序判断左右孩子为空求宽度层序记录每层节点数判断完全二叉树层序空节点标记法。这张表在复习后期非常顶用能帮你在考场上快速完成“题目场景→解题算法”的映射。2.5 图论模板DFS、BFS与最小生成树408的图论部分代码题相对少一些但DFS和BFS的类代码是常客尤其是以“邻接表”为存储结构的遍历。最小生成树、最短路径近年的考法更偏向“手工模拟”和“算法思想表述”直接让你写完整代码的次数不多但不可不防。图的邻接表存储结构定义比较长考场上如果题目没给结构定义你需要自己写出来。这个定义本身也是考点务必默写#define MaxVertexNum 100 typedef struct ArcNode { int adjvex; struct ArcNode *next; } ArcNode; typedef struct VNode { char data; ArcNode *first; } VNode, AdjList[MaxVertexNum]; typedef struct { AdjList vertices; int vexnum, arcnum; } ALGraph;DFS递归模板花几分钟背下来能覆盖“判断两个顶点之间是否存在路径”“输出从u到v的所有简单路径”“判断图是否连通”等变形bool visited[MaxVertexNum]; void DFS(ALGraph G, int v) { visited[v] true; // 访问顶点v printf(%d , v); for(ArcNode *p G.vertices[v].first; p ! NULL; p p-next) { int w p-adjvex; if(!visited[w]) { DFS(G, w); } } } void DFSTraverse(ALGraph G) { for(int i 0; i G.vexnum; i) { visited[i] false; } for(int i 0; i G.vexnum; i) { if(!visited[i]) { DFS(G, i); } } }这段模板里外层for循环是很多非科班同学容易漏掉的。它保证了当图不是连通图时每个连通分量都会被访问到。单独调DFS(G, 0)只能遍历一个连通分量这在判断整个图的连通性时会漏判。BFS模板核心是队列加visited数组思路和树的层序遍历如出一辙只是多了visited判断防止重复访问void BFS(ALGraph G, int v) { SqQueue q; initQueue(q); visited[v] true; enQueue(q, v); while(!isQueueEmpty(q)) { int u; deQueue(q); // u 出队顶点 printf(%d , u); for(ArcNode *p G.vertices[u].first; p ! NULL; p p-next) { int w p-adjvex; if(!visited[w]) { visited[w] true; enQueue(q, w); } } } }关于图的最小生成树和最短路径我的建议是不要死记Prim、Kruskal、Dijkstra、Floyd的完整代码而是重点掌握它们的算法思想表格和手工模拟过程。408主观题更常见的是给你一个图让你用Prim或Kruskal写出最小生成树的边序列或者用Dijkstra写出从源点到各顶点的最短路径过程。完整代码的概率远低于线性表和树所以时间紧张时优先级可以往后放。话说回来如果你是冲刺140的那批人Dijkstra的代码还是建议看懂并能手写出来毕竟大纲没有明确说不考。但普通人备考先保证DFS、BFS和遍历数组的代码分拿到手再去啃最短路径的细节。2.6 查找与排序模板二分、快排、堆排查找和排序模板是性价比最高的一部分。为什么因为思路固定、代码固定、边界条件固定而且每年或多或少都会在选择题里分析复杂度在大题里写出过程或者代码。二分查找模板这个必须刻进脑子里int binarySearch(int a[], int n, int key) { int low 0, high n - 1; while(low high) { int mid (low high) / 2; if(a[mid] key) return mid; else if(a[mid] key) low mid 1; else high mid - 1; } return -1; }这个模板的细节在于while(low high)带等号因为当low high时mid指向的元素还有可能是target必须再判断一次。另外mid (low high) / 2在数组长度很大时有溢出风险更严谨的写法是low (high - low) / 2。408考场上不会考溢出这种极端刁钻的点但写出来会显得你很专业。快速排序是目前408出镜率最高的排序算法。重点掌握它的一次划分Partition函数和整体递归结构int Partition(int a[], int low, int high) { int pivot a[low]; while(low high) { while(low high a[high] pivot) high--; a[low] a[high]; while(low high a[low] pivot) low; a[high] a[low]; } a[low] pivot; return low; } void QuickSort(int a[], int low, int high) { if(low high) { int pivotpos Partition(a, low, high); QuickSort(a, low, pivotpos - 1); QuickSort(a, pivotpos 1, high); } }快排模板需要记的边界点有两个一是while(low high)内层的两个while必须也要判断low high否则high可能一路减到low左边去二是a[high] pivot这里的等号不能随便去掉去掉等号后如果数组里有大量和pivot相等的元素会左右反复交换虽然最终结果对但效率会退化面试和考试时容易被追问。堆排序的筛选向下调整模板常考的是“建堆”和“堆排序过程”。408选择题高频让你判断某序列是否为堆大题偶尔让你写出堆排序每趟的结果。代码模板掌握“调整一个节点”和“建堆”两层void AdjustDown(int a[], int k, int len) { int tmp a[k]; for(int i 2 * k 1; i len; i 2 * i 1) { if(i 1 len a[i] a[i 1]) i; if(tmp a[i]) break; else { a[k] a[i]; k i; } } a[k] tmp; } void BuildMaxHeap(int a[], int len) { for(int i len / 2 - 1; i 0; i--) { AdjustDown(a, i, len); } }小根堆的调整只需要把a[i] a[i 1]改成a[i] a[i 1]把tmp a[i]改成tmp a[i]。模板不需要背两遍背一遍然后知道对称修改即可。2.7 KMP模板next数组的推导KMP算法是408考纲中的难点也是容易出大题的地方。我见过很多同学在这块反复挣扎原因其实不是KMP本身难而是网上的讲解版本太多不同资料里next数组的定义不一致。严蔚敏教材和408考纲用的next数组定义是next[j]表示当模式串中第j个字符失配时模式串应该回退到的位置下标模式串下标从1开始时next[1]0。王道书常用另一种下标起点。如果你混着看绝对绕晕。408考场上我的建议是锁定一种写法反复推导练习。我推荐使用严蔚敏教材的下标从1开始版本因为真题的标准答案基本以这个为基准void getNext(char T[], int next[]) { int i 1, j 0; next[1] 0; while(i T[0]) { // T[0]存模式串长度T从下标1开始 if(j 0 || T[i] T[j]) { i; j; next[i] j; } else { j next[j]; } } }KMP匹配函数int KMP(char S[], char T[], int next[]) { int i 1, j 1; while(i S[0] j T[0]) { if(j 0 || S[i] T[j]) { i; j; } else { j next[j]; } } if(j T[0]) return i - T[0]; else return 0; }这段代码看着短但它是考场上最容易“一写就废”的代码之一。为什么因为next数组的推导是动态规划思想失配时j next[j]这一步很多人会写成j 0那就退化成了朴素匹配。建议备考时至少把三到五个经典模式串比如“abaabcac”“ababaaab”等的next数组手算五遍以上再配合代码默写基本就能稳定拿分了。3. 被多数人忽略的模板细节边界、复杂度与手写规范模板背熟只是第一步考场上能不能拿分还取决于你是不是清楚每个模板的边界条件和复杂度。下面这几个问题是我自己复习后期总结的建议对照自查。3.1 边界条件自查表我把408高频模板的边界条件整理成一张表你可以打印出来贴墙上每天扫一眼模板最常错的边界/细节正确写法要点顺序表删除数组下标越界遍历条件是i length不是i length链表反转断链必须先用next保存cur-next再改cur-next链表删除pre是否需要移动删除时pre不动保留时precur循环队列队满判满方式(rear 1) % MaxSize front否则假溢出树的高度空树返回值空树返回0不是-1层序遍历队列元素类型队列存的是节点指针不是int二分查找循环条件low high不能写成low high快排划分内层边界判断内层while也要low high不能丢KMP求next失配回退回退到next[j]不是回退到0这些边界条件不是靠“读题”能发现的拼的就是平时是否刻意记过。我在冲刺阶段每天花10分钟默写一张边界表效果比多刷三套卷子还好。3.2 时间复杂度和空间复杂度的标准表述408代码题一般不会只要求“写出代码”很多问法带一句“并说明你所设计算法的时间复杂度和空间复杂度”。所以模板旁边必须标注复杂度而且是标准表述不是含糊的“大概是O(n)”。高频模板的复杂度速查顺序表按值删除、链表逆置、链表删除指定值、二分查找O(n)、O(1)二叉树遍历递归版O(n)、O(高度)最坏O(n)一般O(log n)到O(n)层序遍历O(n)、O(n)队列最多存一层节点排序平均情况快排O(n log n)、O(log n)堆排序O(n log n)、O(1)归并排序O(n log n)、O(n)KMP求next和匹配O(mn)、O(m)注意408里说的空间复杂度一般是指额外辅助空间。快排的空间复杂度O(log n)指的是递归栈深度而不是开了额外数组归并排序O(n)是辅助数组。这些描述如果你能在答题时主动写“额外空间复杂度为O(1)”阅卷观感就很专业。3.3 手写代码的规范问题考场上手写代码不需要到编译器里跑通但需要逻辑自洽。手写规范上我有几个实用建议第一代码里的函数名和变量名没有硬性要求但建议和教材保持一致。比如Partition、QuickSort、DFS、BFS阅卷老师看到就明白你在写什么比自创一个f()好得多。第二如果题目没有给出结构体定义你需要自己先把结构体定义写清楚再写核心逻辑。结构体定义在阅卷里也有分不要省略。有些同学图省事直接写“假设链表已存在”——这会让阅卷人怀疑你到底会不会定义存储结构。第三核心变量的初始化别省略。visited数组不初始化就是随机值队列不初始化front/rear就乱套这些步骤写在代码里也就一两行但体现了你是否真正理解算法运行的前提。第四有时候可以适当用注释补充核心思路。比如在循环队列判满那行注释一句“牺牲一个存储单元区分队空队满”会让解答更有说服力。4. 真题场景训练从模板到模式识别的三步走模板整理完毕、边界条件熟悉之后最关键的环节出现了你得能在考场上把一个“看起来没见过”的题目映射到某个模板上。这个能力不是靠背模板来的是靠真题场景训练来的。我建议复习后期按以下三步走第一步把近十年408真题里的代码题全部摘出来不要写答案先给每个题目标注“它能套用哪个模板”。标注的时候不要只标一个要标出所有可能的方向。比如一道“删除链表中重复节点”的题既可能套“链表删除”模板也可能套“顺序表双指针”思想。多角度标注能训练你灵活迁移的能力。第二步把同一模板下的真题集中做一遍。比如“链表反转”这个模板你找到了真题里的反转链表、真题里的从尾到头输出、真题里的倒序合并那就一天之内把这几个题全部刷完。每个题目的变化点记在旁边总结模板在哪些地方需要微调。第三步掐时间模拟考场。找一套没做过的真题模拟卷代码题全部用手写在A4纸上设定时间限制一般一道代码题15-20分钟。写完之后用评分标准给自己打分。这一步不是为了测正确率是为了测你“在时间压力下手写代码”的熟练度顺便练字迹和排版。我印象特别深的是有一年408考了一道“找出两个链表的第一个公共节点”的改编题。乍一看似乎没见过但它本质上就是两个链表长度差的问题和“合并两个有序链表”一样属于链表基础操作的变体。当时我脑子里的模板库里没有“公共节点”这个模板但我知道链表题的通用套路先求长度、再长链先走差值、最后同步遍历。这就是模板之上的算法思维它是从大量模板练习中长出来的不是凭空冒出来的。很多人喜欢在网上找各种“秒杀技巧”和“绝密模板”我个人经验是与其看十份资料不如把自己手上的模板吃透。408的代码题考点非常集中几乎不会超出教材的例题范畴。你只要把上面提到的线性表、链表、栈队列、树、图、查找、排序这七个板块的模板真正消化掉真题里的代码题基本都能覆盖。5. 时间规划与避坑经验从九月到考前的模板复习节奏最后聊一下时间安排和常见的备考误区这部分是我踩坑踩出来的希望对正在备考的同学有直接帮助。5.1 三轮复习中的模板任务408的复习周期通常从三月或六月开始但“算法模板”的系统整理不需要这么早过早整理很容易因为还没形成知识网络而白费功夫。我建议把模板学习拆成三个阶段基础阶段3月-7月跟随教材和网课学习每个章节同步整理每个章节的核心代码。这个阶段不要求背但要求理解。每学完一个数据结构自己把教材上的例题代码敲一遍运行通过后放在模板本里。我在这个阶段用的是王道单科书每章的课后代码题都敲一遍尤其是数据结构的定义和基本操作。强化阶段8月-10月开始集中背默模板。每周挑两天专门做“模板默写日”不看书不查资料手写五个模板并用边界自查表核对。这个阶段的目标是把模板从“看得懂”变成“能默写”。如果发现某个模板经常默写错就把它单独拎出来在A4纸上连续默写三天直到零失误。冲刺阶段11月-12月用真题和模拟题做模板的迁移训练。不再默写模板本身而是做整套卷子时遇到代码题就手写完整代码写完对照标准答案看自己的代码是否有多余步骤、是否漏了边界条件。这个阶段我还把之前整理的那张边界自查表贴在书桌前每天做卷子前扫一眼非常有用。5.2 常见避坑清单第一个坑过度依赖视频课而不动手写。数据结构代码题看十遍不如写一遍。很多同学看网课觉得老师写得轻松自己一上手就卡壳。解决方法是看完一个算法的视频马上合上书手写代码写不出来再看看完再合上重复三遍基本就能内化。第二个坑只看408真题不做课后扩展题。408代码题虽然考点集中但每年总有那么一两道题有一点变形。如果你只刷408真题可能会陷入“固定套路”的舒适区。我的建议是适当做王道单科书里的课后题它的难度和风格与408比较接近更重要的是帮你在模板之外积累一些“非典型变形”。第三个坑忽视手写速度。考场上一道代码题15分钟到20分钟加上读题、想思路实际纯书写时间可能只有10分钟。我见过有同学草稿纸上写得飞快正式答题纸上慢得像绣花。平时模拟时就应该练速度一道10行左右的代码题从开始写到收笔控制在8分钟以内这是比较稳妥的节奏。第四个坑数据结构定义背不熟。很多同学上来就背核心算法代码结果考场上需要自己写存储结构定义时卡壳了。记住408代码题存储结构定义是基础分也是算法代码的前提。顺序表、链表、二叉树、邻接表这四种结构定义必须达到“盲写”水平。5.3 冲刺阶段的每日模板安排最后说一个我当年冲刺阶段每天坚持的方案仅供参考每天早上开考前模拟前花15分钟默写三个模板一个数据结构的存储结构定义一个核心算法代码一个边界条件自查表。模板的内容按章节轮换比如周一是线性表和链表周二是栈队列和树周三是图和查找周四是排序和KMP周五回归综合。到考前一周基本所有模板都能在两分钟内完成结构定义加核心代码的默写那种“手上有粮心里不慌”的感觉就出来了。很多同学问我这么机械的重复值得吗我的回答是在408考场上代码题的时间压力和心理压力下你不可能像平时一样慢悠悠地分析边界条件。唯一能依靠的就是肌肉记忆一样的模板反应。模板不是为了让你变成一个只会套代码的木偶而是为了把那些不需要临场发挥的部分固化下来把宝贵的思考时间留给真正需要变通的部分。我个人的体会是模板整理得越早后期刷真题就越从容。它不像数学题需要大量新题开眼界代码题更像一门手艺熟能生巧。如果你现在还没开始整理模板从今天起拿一张A4纸从线性表的定义和删除操作开始写下你的第一个模板一路坚持到考前你会感谢这个决定的。