程序设计与算法基础II期末复习:考点地图、代码模板与备考策略

发布时间:2026/9/18 10:54:38
程序设计与算法基础II期末复习:考点地图、代码模板与备考策略
期末备考这件事最忌讳的就是拿着课本从头翻到尾到头来代码写不出来概念背了一堆却不知道用在哪。尤其是《程序设计与算法基础II》这种MOOC课平时作业跟着平台走可能还没什么感觉一到期末考试就暴露问题选择题考概念细节填空题考代码补全编程题考能不能把算法模板默写出来并改对。我结合自己带过几届学弟学妹复习的经验把这门课的考点、代码模板和临场技巧完整梳理一遍希望能帮正在备战的你少走弯路。1. 这学期到底学了什么课程考点全貌与复习主线先明确一个认知这门课叫“程序设计与算法基础II”重点不在“语言语法”而在“算法思维”。C/C的语法能力是前置基础真正拉开分差的是你对数据结构、经典算法、复杂度分析的掌握程度。1.1 考点地图从线性表到图论算法我把整门课的核心考点整理成了一张复习地图你可以对着自查知识模块核心考点常见考查形式线性表顺序表与链表操作、插入删除边界处理代码补全、读程序写结果栈与队列栈的应用括号匹配、表达式求值、循环队列判空判满选择题、简答题树与二叉树遍历先序/中序/后序/层序、树与二叉树的转换、哈夫曼树与编码构造题、计算题图存储结构邻接矩阵/邻接表、DFS/BFS、最小生成树Prim/Kruskal、最短路径Dijkstra/Floyd代码填空、模拟手动推演查找二分查找、二叉排序树、哈希表与冲突处理边界条件分析、哈希表构造排序冒泡、插入、选择、快排、归并、堆排序排序过程模拟、时间复杂度对比、代码补全算法设计递归与分治、贪心、动态规划、KMP算法设计题、编程题对照这张表你会发现考试不会只考“背概念”而是要求你在限定时间内完成代码层面的应用。所以如果现在距离考试还有一周以上我建议按“数据结构基础→经典算法→高频代码模板→OJ刷题”的顺序复习而不是按MOOC章节顺序刷视频。1.2 为什么这门课容易挂三大常见误区带过不少复习的同学我发现挂科或者低分飘过的人通常踩了这三个坑第一个坑是只刷MOOC课后的选择题从来不亲手写代码。MOOC的客观题能帮你记概念但期末考试里的编程题和补全代码题你没敲过代码是写不出来的。特别是递归、指针、动态规划这种抽象内容眼睛看会了和手会写是两码事。第二个坑是忽视复杂度分析。考试里经常给你几段代码让你判断时间复杂度或者让你选一个在大数据量下不会超时的排序算法。如果你连O(n²)和O(n log n)的区别都没概念这类题基本靠蒙。第三个坑是死记模板不理解原理。比如快排的partition步骤、堆排序的adjustDown过程这些代码你背下来不难但题目稍微变形比如让你用非递归写快排、让你用堆求第k大元素不会变通就直接丢分。2. 排序与查找性价比最高的拿分模块排序和查找为什么放在最前面复习因为这部分既好拿分又几乎必考。编程题里排序是其他算法的基础选择题里复杂度和稳定性对比是送分题代码补全题里二叉搜索树和二分查找的边界条件是高频考点。2.1 六大排序算法横向对比先把结论放这里考试考排序90%的选择题都绕不开这张表排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性冒泡排序O(n²)O(n²)O(1)稳定直接插入排序O(n²)O(n²)O(1)稳定简单选择排序O(n²)O(n²)O(1)不稳定快速排序O(n log n)O(n²)O(log n)~O(n)不稳定归并排序O(n log n)O(n log n)O(n)稳定堆排序O(n log n)O(n log n)O(1)不稳定需要特别提醒的几点快排的最坏情况是待排序序列已经有序或逆序此时每次划分只减少一个元素退化成O(n²)。考试如果问“什么情况下快排效率最低”答案不是“数据量大的时候”而是“基本有序的时候”。优化办法是取中间元素或随机元素作为基准而不是固定取第一个。堆排序的空间复杂度是O(1)这是它对比归并排序的核心优势。但它的常数因子大实际速度往往不如快排所以不要因为堆排序理论复杂度好看就到处用。教材里常考的“在100万个元素中找前10个最大的数”用堆最合适因为维护一个大小为10的小顶堆复杂度只有O(n log 10)。归并排序的稳定性和O(n)的额外空间是它的标志。归并排序的经典变体是“求逆序对数量”这是分治思想的典型应用——在合并两个有序数组时如果右边元素小于左边元素说明左边剩余元素都比右边这个元素大累加逆序对数量。我见过期末编程题考过这个变体。2.2 冒泡排序与快速排序代码逐行拆解教材讲的冒泡排序很简单但考试有几种变形写法需要注意。经典冒泡核心代码void bubbleSort(int a[], int n) { for (int i 0; i n - 1; i) { bool swapped false; for (int j 0; j n - 1 - i; j) { if (a[j] a[j 1]) { swap(a[j], a[j 1]); swapped true; } } if (!swapped) break; // 本趟无交换说明已有序 } }这里加了一个swapped标志位如果某轮循环没有发生任何交换说明序列已经有序可以提前退出。这个优化在判断“给一个几乎有序的数组排序用什么算法最快”这类选择题里会用到——答案是插入排序或优化后的冒泡排序因为它们的平均复杂度可以接近O(n)。快排是复习的重中之重。我推荐的写法是经典的Lomuto划分或Hoare划分。这里给出一种容易记忆且不易出错的写法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 pos partition(a, low, high); quickSort(a, low, pos - 1); quickSort(a, pos 1, high); } }注意while (a[high] pivot)这个判断里的。如果把写成当数组中有大量重复元素时low和high可能会越界或死循环。这是快排代码补全题最喜欢挖的坑你要是自己写的时候也要养成加等号的好习惯。2.3 二分查找的边界条件一个容易全军覆没的考点二分查找代码很短但边界条件极其容易写错。我见过太多人在while (left right)和while (left right)之间纠结在mid (left right) / 2和mid left (right - left) / 2之间犹豫。推荐一套自己长期使用的模板// 在有序数组中查找target返回下标不存在则返回-1 int binarySearch(int a[], int n, int target) { int left 0, right n - 1; while (left right) { int mid left (right - left) / 2; if (a[mid] target) return mid; else if (a[mid] target) left mid 1; else right mid - 1; } return -1; }核心规则while (left right)对应区间[left, right]每次更新时left mid 1或right mid - 1因为mid已经检查过了。如果写成while (left right)则对应区间[left, right)更新时要用left mid 1和right mid因为mid还没检查。两套写法都对但不要混用。还有一个隐藏考点mid (left right) / 2在leftright很大的时候可能溢出整型范围所以更安全的写法是mid left (right - left) / 2。这个细节在笔试题里偶尔会出现拿不到就可惜了。2.4 哈希表与冲突处理哈希表的考点集中在哈希函数构造、冲突处理方法开放定址法、链地址法、查找成功和失败的平均查找长度计算。以最常见的除留余数法为例假设表长为13哈希函数H(key) key % 13用线性探测再散列处理冲突题目会给你一串关键字让你构造哈希表并计算查找成功时的平均查找长度。这类题是纯粹的套路题关键在于线性探测时遇到冲突要往后找空位找到表尾还没空位就循环回表头。链地址法拉链法则把冲突的元素挂在同一个链表下ASL的计算方式是所有关键字所在链表的查找次数之和除以关键字个数。考试中的哈希表题目一般不难但你要熟练掌握“装填因子”的定义表中记录数 / 表长。装填因子越大冲突概率越高ASL越大。哈希表的ASL只与装填因子有关与表长和记录数的具体数值无关这个点偶尔会作为判断题出现。3. 树、图与算法设计拉开分差的核心模块如果前面的排序、查找是基础分那树、图和算法设计题就是区分“及格”和“优秀”的分水岭。MOOC期末的这些题目往往不是单个知识点的考查而是几个知识点的组合应用比如用二叉树遍历的思想解决问题、用DFS求解连通分量、用动态规划设计状态转移方程。3.1 二叉树的遍历与重建三种深度优先遍历的递归写法是基础中的基础void preorder(Node* root) { if (root NULL) return; visit(root); preorder(root-left); preorder(root-right); }中序和后序只是把visit的位置换一下应该能做到10秒内默写出来。层序广度优先遍历要用队列实现也别忽略void levelOrder(Node* root) { if (root NULL) return; queueNode* q; q.push(root); while (!q.empty()) { Node* cur q.front(); q.pop(); visit(cur); if (cur-left) q.push(cur-left); if (cur-right) q.push(cur-right); } }考试的概率比较高的题目是“已知先序和中序重建二叉树”。题目会给两个序列比如先序ABDECF、中序DBEAFC让你还原这棵树。解法是先序的第一个节点A是根在中序里找到A的位置A左边的DBE是左子树中序序列右边的FC是右子树中序序列然后根据左子树中序长度3回到先序中取A后面3个节点BDE作为左子树的先序序列剩下CF作为右子树先序序列递归处理即可。这种题一定要亲手画几遍不要光看解析。我建议把教材上3-4个例子全部自己推一遍推完你就发现规律很固定。3.2 哈夫曼树与哈夫曼编码一类算起来很爽的题哈夫曼编码在考试里属于纯计算题步骤固定而且结果唯一在有多个最小权值节点时可能有多种合并方式但WPL相同。给你一组权值比如{2, 3, 5, 7, 11}让你构造哈夫曼树并求WPL带权路径长度。计算WPL有一个小技巧不用完整画出每一条路径长度再相乘相加而是每次合并时把两个权值相加并累加到一个sum变量里最后sum就是WPL。比如上面这组权值先合并2和3得到5sum5再合并5和5得到10sum15再合并7和10得到17sum32最后合并11和17得到28sum60。所以WPL60。这个方法比数叶子深度快得多而且不容易错。哈夫曼编码的特性是任一字符的编码都不是另一个字符编码的前缀所以解码时不会产生歧义。这也是信息压缩的基础原理——高频字符用短编码低频字符用长编码整体压缩效率最高。如果考题延伸到“用哈夫曼编码压缩字符串”本质就是求各字符频率并构造哈夫曼树。3.3 图的遍历DFS与BFS的代码与应用图的DFS和BFS在考试中经常以多种形式出现直接考代码、考遍历序列、考求连通分量数量、考拓扑排序、考判断是否有环。DFS的伪代码你需要滚瓜烂熟void dfs(int u) { visited[u] true; for (int v : adj[u]) { if (!visited[v]) { dfs(v); } } }在无向图中一次dfs能访问一个连通分量内的所有顶点。所以如果你要对图求连通分量数量只需要在main里遍历所有顶点每遇到一个未访问的顶点就调用一次dfs并计数计数结果就是连通分量数。这个解题思路在期末编程题里经常出现。BFS适合求最短路径——注意是“无权图的最短路径”每条边权值都是1或等都行。BFS按层扩展能保证第一次访问某个节点时的路径长度最短这个结论在很多图论编程题里都能用上。代码模板是配合队列使用入队时标记visited出队时访问并扩展邻居。要注意标记visited的时机如果出队时才标记同一个节点可能被多个邻居重复压入队列导致效率降低甚至死循环。3.4 最小生成树与最短路径拓扑排序别忽略考试涉及的最小生成树算法有两个Prim和Kruskal。Prim是选点扩展适合稠密图时间复杂度O(V²)用堆优化可以到O(E log V)Kruskal是选边扩展适合稀疏图时间复杂度O(E log E)。大题的常见考法是给你一个图的边集让你用手动模拟两种算法的执行过程最后画出生成树并求权和。Dijkstra算法是单源最短路径的最经典算法要求图中不能有负权边时间复杂度O(V²)堆优化O(E log V)。核心思想是贪心每次从未确定最短路的顶点中选一个距离源点最近的然后用它去松弛相邻顶点。考试会考手动推演标出每一轮加入集合的顶点和更新后的dist数组。Floyd算法则用三重循环求所有顶点对之间的最短路径代码极短适合记忆。拓扑排序经常容易被忽略但它本身不难统计每个顶点的入度把入度为0的顶点加入队列依次出队并减少其邻接点的入度如果邻接点入度变为0则入队。最后如果出队顶点数不等于顶点总数说明图中有环。这个“有环检测”功能让它成了判断课程安排是否合法、任务依赖是否有循环的经典解法。代码题如果考到你只要把入度数组维护好基本就稳了。4. 递归、贪心、动态规划与KMP从模板到灵活变通这个模块是“算法设计题”的主要来源也是最让大一同学头疼的地方。我的建议是先掌握几个经典问题的标准解法然后通过手写代码把套路内化而不是试图在考场上临时推导一个状态转移方程。4.1 递归与分治快慢指针和归并排序的变体递归的知识点常常结合链表和数组来考。递归的核心是明确“递归函数要完成什么功能”以及“递归终止条件”。期末复习时我建议掌握以下几类递归题目链表反转递归版归并排序分治思想二分查找递归版求最大公约数辗转相除法汉诺塔问题归并排序的分治代码是一个标准的递归模板void mergeSort(int a[], int l, int r) { if (l r) return; int mid (l r) / 2; mergeSort(a, l, mid); mergeSort(a, mid 1, r); merge(a, l, mid, r); }后面这个merge函数就是把两个有序数组合并成一个有序数组考试常考merge的实现。注意要开一个临时数组来存合并结果最后再复制回原数组。如果你在期末编程题里写归并排序千万别忘了这一步否则排序结果不在原数组里。4.2 贪心算法证明是难点套路是重点贪心算法在考试里常以三种题型出现活动安排问题、哈夫曼编码问题、背包问题部分背包不是01背包。活动安排问题的核心思路是按结束时间从小到大排序然后依次选择与前一个已选活动不冲突的活动。这个思路简单但考试偶尔会问“为什么按结束时间排序是最优的”你需要能说出贪心选择性质的直观解释——结束时间越早留给后续活动的空间越大。部分背包问题物品可以分割可以用贪心按单位重量价值从高到低排序优先装单位价值最高的。但如果物品不可分割01背包贪心就不是最优解了必须用动态规划。这个区别是高频判断题01背包为什么不能用贪心因为贪心只考虑了局部最优而01背包的全局最优需要综合考虑组合不能简单按单位价值排序。贪心算法的大题往往还会让你写“反例”。比如找零问题中如果硬币面额是{1, 5, 11}要凑15元贪心先取11再取1111需要5枚硬币而最优解是555只要3枚。这种反例能有效检验你是否真正理解了贪心算法的局限性。4.3 动态规划从背包问题到最长公共子序列动态规划是这门课里最难也最常考的大题每年期末编程题基本都会有一道。复习重点放在这几类01背包问题是理解DP的经典入口。状态定义是dp[i][j]表示前i件物品放入容量为j的背包能获得的最大价值。状态转移方程是dp[i][j] max(dp[i-1][j], dp[i-1][j-w[i]] v[i]) // j w[i] dp[i][j] dp[i-1][j] // j w[i]这个转移方程的含义第i件物品要么不选继承前i-1件在容量j下的结果要么选价值加上剩余容量j-w[i]下前i-1件的结果。考试不仅会考代码还会考填表过程——给你物品重量和价值让你画出dp表。这时候关键是行对应物品编号、列对应容量0到总容量逐行填写。01背包的一维优化也需要了解用滚动数组dp[j]表示容量为j时的最大价值但内层循环必须从大到小遍历容量。如果从小到大遍历同一个物品会被多次选择就变成了完全背包。这是期末的高频坑点。完全背包的遍历顺序则是从小到大正好和01背包相反注意对比记忆。最长公共子序列LCS的状态转移方程是另一大考点dp[i][j] dp[i-1][j-1] 1 // a[i] b[j] dp[i][j] max(dp[i-1][j], dp[i][j-1]) // a[i] ! b[j]考试一般会让你填这个二维表并求LCS长度偶尔还会要求输出具体的公共子序列。输出子序列需要在计算dp的同时用另一个数组记录每个位置是从哪个方向转移来的然后从右下角递归回溯构建子序列。最长上升子序列LIS可以用O(n²)的简单DP也可以用贪心二分优化到O(n log n)后者在时间限制紧的题目里可能用到但作为期末复习先把O(n²)模板写对更重要dp[i] 1; for (int j 0; j i; j) { if (a[j] a[i]) dp[i] max(dp[i], dp[j] 1); }4.4 KMP算法手算next数组和代码模板KMP是很多同学的噩梦但期末考点相对固定。我个人觉得KMP难的其实是next数组的手算一旦掌握了规则KMP就是一套固定流程先算next数组再用next数组进行匹配匹配失败时模式串指针回退到next[j]而不是从头开始。next数组的语义next[j]表示模式串中第j个字符之前的子串中最长相等前后缀的长度。注意有些教材的next数组从1开始且next[1]0有些实现从0开始且next[0]-1考试答题前先看清题目约定否则容易失分。用手算的方法举个例子。模式串ABABABC从1开始编号位置j字符前缀子串最长相等前后缀长度1A空02BA03AAB04BABA1前缀A后缀A5AABAB2前缀AB后缀AB6BABABA3前缀ABA后缀ABA7CABABAB4前缀ABAB后缀ABAB如果记不住这个推导过程有个更直接的手算技巧看每个位置之前子串的最长相等前后缀长度。前缀和后缀不能是子串本身而且前缀必须从第一个字符开始后缀必须到最后一个字符结束。多练几组字符串就会发现规律很快。KMP的匹配代码模板int kmp(string s, string p) { int n s.size(), m p.size(); vectorint next(m, 0); // 计算next数组 for (int i 1, j 0; i m; i) { while (j 0 p[i] ! p[j]) j next[j-1]; if (p[i] p[j]) j; next[i] j; } // 匹配 for (int i 0, j 0; i n; i) { while (j 0 s[i] ! p[j]) j next[j-1]; if (s[i] p[j]) j; if (j m) return i - m 1; } return -1; }这个版本用next[j-1]实现回退next[0]初始化为0和教材的传统写法略有不同但思路一致。建议你选择一种自己理解的版本背熟不要考试时临时换版本。5. 考前实战编程题怎么练MOOC期末模拟怎么做光看知识点不动手写代码你永远不知道自己会在哪里卡住。考前一周我的建议是每天至少手写3道代码题不需要上机拿纸笔直接写完整代码。这样能模拟考场环境还能训练手速。5.1 高频编程题清单与练习顺序按出现概率从高到低排列有以下几类题值得重点练习二分查找含变体在有序数组中找第一个大于target的位置冒泡/快排/归并排序的手写代码链表反转迭代和递归两版二叉树前中后序和层序遍历图的DFS求连通分量01背包二维和一维优化都要会最长公共子序列LCSKMP匹配并查集偶尔会出现用于判断图的连通性大整数加法或乘法电信软院的题偶尔涉及练练没坏处每天抽出两小时上午一小时看错题和概念晚上一小时挑战2-3道代码题。练习时不用借助IDE的自动补全纯手写。写的代码要包含必要的头文件、函数签名和返回值格式要完整因为考试判分有时会看格式分。5.2 上机环境与时间分配策略MOOC期末如果包含编程题通常是在线判题系统上提交代码。考前一定要确认你用的编译器版本比如C11是否支持#include bits/stdc.h以及系统对数组越界、内存超限的惩罚规则。实测下来大部分MOOC平台的判题环境用C11的万能头文件没问题但个别老平台不支持保守起见你还是要会写#include iostream、#include vector等具体头文件。时间分配上我建议开考后先花3分钟浏览全卷把题目分成“会做”“有点思路”“完全不会”三类。选择题和填空题控制在25分钟内解决代码补全和简答题控制在35分钟剩下的时间全部留给编程题。编程题如果第一题卡了10分钟还没思路果断跳过先保证拿到会做的分数再回头攻克难题。关于编程题debug如果线上提交后显示编译错误先检查是不是少写了分号或者头文件如果显示答案错误优先检查边界条件——比如数组下标是否越界、循环条件是否漏了等号、n1或空数组的特殊情况是否处理。很多人明明思路正确就是边界条件考虑不全导致过不了全部测试点。5.3 考前72小时冲刺计划第72小时到48小时过一遍所有算法模板手写快排、归并、Dijkstra、LCS、KMP各一遍。写完对照教材改错。这时候改动成本最低也最能暴露出你记忆模糊的地方。第48小时到24小时做一套往年的期末真题或MOOC平台上的模拟卷。严格按照考试时间计时不要翻书不要用编译器自动补全。做完之后把错题分类汇总发现自己最薄弱的知识模块集中看笔记和教材对应章节。考前最后一天不再学习新题目。把常见的时间复杂度表、排序稳定性表、算法的适用条件背一遍。把几种容易混淆的算法拿出来对比记忆快排和归并的空间复杂度为何不同、Prim和Kruskal分别适合什么图、01背包和完全背包的遍历顺序区别。状态不好的时候就看看代码模板不勉强自己刷题。5.4 考场心态与时间管理技巧进考场那一刻起你的目标不是考满分而是把会做的全做对、不会做的尽量得分。遇到不会的选择题用排除法去掉明显错的选项再在剩余选项里凭知识直觉选一个。填空题如果拿不准可以试一试特殊值——比如把n1或n2代入题目给的场景看哪个结果合理有时候能反向推出答案。所有的编程题先写框架再补细节。先定义主函数和必要的变量把核心逻辑写出来最后回头补头文件和边界条件。判题系统看的是最终代码的正确性不是你的思考过程所以即使代码写得乱只要AC就是胜利。如果最后还有多余时间一定不要提前交卷。把每道程序题的代码从头到尾重新读一遍重点检查循环变量有没有写错、数组下标有没有越界、比较符号有没有写反。我见过太多人明明会做因为一个i和j写混了丢了整题的分那种丢分太可惜了。6. 易错点与避坑指南来自真实考试现场的教训这些坑不是我凭空想出来的是历届同学在MOOC期末中真实踩过的。提前知道这些坑你至少能少丢10分。6.1 排序与查找模块的三个经典错误第一个错误是快排递归的终止条件写错。有人写成if (low high) return;这对单元素区间没问题但如果调用quickSort时传入的区间是空的比如low high就会递归爆栈。正确写法是if (low high) return;或者if (low high)才递归。第二个错误是二分查找的更新边界和循环条件不匹配。前面说过while (left right)要搭配right mid - 1和left mid 1while (left right)要搭配right mid。很多人混着写最后程序要么死循环要么漏掉元素。第三个错误是堆排序的adjustDown循环条件。堆排序的核心调整函数中循环条件是while (i * 2 1 n)假设数组下标从0开始即左孩子存在。有人写成while (i * 2 n)当下标从0开始时就会漏掉右孩子的比较排序结果错误。这种细节题在代码补全里极其常见。6.2 图与树模块的高频扣分点遍历二叉树时有人忘记了处理空节点直接在visit(root)前不判断root是否为NULL。递归版的前序遍历如果忘写if (root NULL) return;递归调用在空节点上会访问非法内存程序直接崩溃。这一点在纸笔写代码时看不出来但上机判题时会直接RE。用Dijkstra算法手动推演时很多人会在“每次从未确定集合中选dist最小节点”这一步选错。别忘了已经确定最短路的节点不能重复选。如果你在一个已经确定过的节点上继续松弛会导致答案错误甚至逻辑混乱。每轮更新完要标记本轮选择的节点为“已确定”。在Kruskal算法中判断加入一条边是否会形成环需要用到并查集。考试如果让你手算Kruskal过程不能光看边的权值从小到大加还要保证不形成环。很多人在手动推演时漏了“判环”这一步导致最后形成的不是树而是带环图。6.3 动态规划模块的典型失分原因状态定义不清晰是很多人丢分的主要原因。比如01背包题有人把dp[i][j]的含义记错成“前i件物品总重量恰好为j时的最大价值”但实际应该是“前i件物品放入容量为j的背包能获得的最大价值”。“恰好”和“不超过”的区别会导致初始化方式完全不同“恰好为j”时dp[0][0]0其余为负无穷“不超过j”时dp[0][j]全为0。考试时一定要看清题目问的是哪种。LCS的填表顺序错误也会导致整道题白写。计算dp[i][j]依赖dp[i-1][j-1]、dp[i-1][j]、dp[i][j-1]三个方向所以填表顺序必须是外层循环遍历第一个字符串内层循环遍历第二个字符串逐行逐列填充。如果两个字符串的下标搞反后面输出子序列的时候也会跟着错。数组没有开够大小是编程题最常见的RE原因。题目说n最大是1000你却开了int dp[100][100]一旦测试数据是1000程序直接越界崩溃。做题前先看数据范围开数组时养成“按最大值开并多加5”的习惯比如n1000就开dp[1005][1005]。7. 最后的真心话这门课到底在训练什么前几天有个学弟问我说“学长我这学期《程序设计与算法基础II》感觉学得很乱考前应该抓什么”我问他平时作业代码是自己写的还是抄的他说一半一半。我建议他先把所有算法模板从头到尾手写三遍再去做往年的卷子。他照做了最后出分86不算高但从他平时的状态看已经进步很多了。我想说的是这门课真正要培养的不是“背代码的能力”而是“把问题抽象成算法模型”的思维。将来你学操作系统、学网络、学数据库甚至工作中写业务代码都会遇到排序、查找、图论、动态规划的影子。你现在在MOOC期末里多花的时间都是在给后面的课程打地基。复习时别总想着“我是不是来不及了”。只要还没进考场就还有机会。从今天开始每天手写几个算法的完整代码对着教材改错比焦虑十个小时有用得多。最后分享一个我自己的习惯考前一晚把所有算法的代码模板写在纸上第二天早起到考场前再快速浏览一遍。这些模板包括快排、归并、二分查找、二叉树遍历、DFS/BFS、Prim、Dijkstra、01背包、LCS、KMP。看的时候不用逐字背重点是让大脑在考前形成一个“算法索引”考到排序我能立刻想起partition怎么写考到最短路径我能想起dist数组的更新规则。这个“索引”能帮你节省大量读题后重新回忆的时间把更多时间留给真正的思考和调试。祝你期末顺利。