简介:这份资源面向ACM竞赛选手与算法学习者,系统整理了51个经典算法实例,覆盖递归、图论、动态规划、搜索与组合优化等核心方向,适合希望夯实算法基础、提升编程思维的中级学习者对照练习。压缩包内为1个doc文档,约1.77MB,共126页,按目录逐题编排,每题包含题目说明、解题思路、分析过程与可运行源码,形成从理解到实现的完整闭环。内容从河内之塔、费式数列、巴斯卡三角形等入门递归题,延伸到八皇后、骑士走棋盘、老鼠走迷宫等搜索与回溯问题,再到背包问题、蒙地卡罗法求PI、Eratosthenes筛选求质数、超长整数运算等进阶主题,并涉及双色三色河内塔、生命游戏、字串核对等拓展案例。已有413人学习,适合按目录顺序精读或针对薄弱算法专项突破,借助现成源码验证思路、积累可复用的解题模板。
1. 从“河内之塔”这道递归题说起:51个经典算法到底考什么
很多人第一次在笔试里碰到“河内之塔”,会下意识去数盘子、推步骤,结果时间全耗在草稿纸上。真正拿分的做法只有一句话:把 n 个盘子的移动,拆成“先把上面 n-1 个挪到辅助柱、再把最大的挪到目标柱、最后把 n-1 个挪回来”。这道题考的不是你手算快不快,而是你能不能一眼看出递归结构,并写出2^n - 1这个次数结论。这份《ACM51个经典算法大全》收录的 51 个例子,本质上就是 51 次这样的“结构识别训练”:递归与回溯、排序与查找、大数与数论、矩阵与组合,四类考点反复出现。它适合已经会写基本语法、但在真题里总是“想得到却写不对”的备考者,也适合面试前需要快速把经典题型过一遍的人。下面按考点模块拆开讲,每类都落到真题怎么答、易错在哪。
2. 递归与回溯高频考点:从迷宫到八皇后的答题模板
2.1 递归三件套:终止条件、当前层动作、递归方向
这一模块的题,面试官最想听的是你有没有把递归写成“可终止、可回退”的结构,而不是背代码。以老鼠走迷宫为例,二维数组里 2 表示墙、0 表示可走、1 表示路径,入口(1,1)、出口(5,5)。单路径版本的答题思路是:进入一格先标记为 1,判断是否到出口,没到就按右、下、左、上四个方向依次试探,某个方向能走就递归进去;如果四个方向都走不通,把当前格改回 0 再返回。这里的关键动作是“失败要擦掉脚印”,否则回溯时会把死路当成通路。
int visit(int i, int j) { maze[i][j] = 1; // 标记当前格已走 if (i == endI && j == endJ) // 终止条件:到达出口 success = 1; if (success != 1 && maze[i][j+1] == 0) visit(i, j+1); // 右 if (success != 1 && maze[i+1][j] == 0) visit(i+1, j); // 下 if (success != 1 && maze[i][j-1] == 0) visit(i, j-1); // 左 if (success != 1 && maze[i-1][j] == 0) visit(i-1, j); // 上 if (success != 1) maze[i][j] = 0; // 死路回退,擦掉标记 return success; }这段代码里success是全局标志,一旦找到一条路就不再继续试探其他分支,这是“求一条路径”和“求所有路径”的分水岭。参数i、j是当前坐标,四个if的顺序决定搜索方向,顺序本身不影响正确性,但会影响找到路径的快慢。面试时如果被追问“求所有路径怎么改”,答案是把success判断去掉,到达出口就打印当前maze,然后继续递归,最后统一把maze[i][j]置回 0——这正是文档里“老鼠走迷宫(二)”的做法。
2.2 八皇后:分支修剪是加分项,不是可选项
八皇后是回溯题里最常被拿来问“你怎么优化”的一道。朴素做法是每行放一个皇后,逐列尝试,放之前检查同列、两条对角线是否冲突。文档里的写法用三个数组column[]、rup[]、lup[]分别记录列、右上到左下、左上到右下是否被占用,放子时置 0,回溯时置 1。答题时要把“为什么用三个数组”讲清楚:如果每次都用循环去扫棋盘,复杂度会多一个 N,而用数组做 O(1) 冲突判断,就是所谓的“分支修剪”。
void backtrack(int i) { if (i > N) { showAnswer(); } // 8 行都放完,输出一组解 else { for (int j = 1; j <= N; j++) { if (column[j] == 1 && rup[i+j] == 1 && lup[i-j+N] == 1) { queen[i] = j; // 第 i 行皇后放在第 j 列 column[j] = rup[i+j] = lup[i-j+N] = 0; // 占用 backtrack(i+1); column[j] = rup[i+j] = lup[i-j+N] = 1; // 回退 } } } }i-j+N这个偏移是为了让数组下标不为负,这是手写代码时最容易翻车的地方。面试官常追问“八皇后一共多少解”,标准答案是 92 组解,其中去掉对称后是 12 组本质不同的解。能答出这个数字,说明你不是只会套模板。
2.3 骑士走棋盘:Warnsdorff 规则怎么讲才不空
骑士旅游题如果只答“用递归试八个方向”,面试官会认为你没读过优化解法。文档里给出的思路是 Warnsdorff 规则:每一步优先走“下一步出路最少”的那个格子。答题结构可以分三层:先定义八个方向偏移ktmove1、ktmove2;再对每个候选格统计它还能走几步,记在exists[]里;最后选exists最小的方向落子。这样即使不用递归,也能较高概率走完 64 格。要注意的是这条规则是启发式,不保证一定成功,答题时主动说出“不保证”反而是加分项,说明你理解算法边界。
3. 排序与查找真题:手写快排和二分最容易丢分的细节
3.1 三种快排写法,面试到底要你写哪种
文档把快速排序拆成(一)(二)(三)三个版本,这不是凑数,而是对应面试里三种常见追问。版本一用最直观的“取第一个元素当基准,左右指针向中间夹”;版本二改基准选取;版本三处理更细的划分。答题时先给结论:快排平均 O(n log n),最坏 O(n²),不稳定。然后按“选基准、划分、递归左右”三步写。手写时最容易被挑毛病的是边界:left < right还是left <= right,基准元素要不要参与下一轮递归。一个稳妥的答法是划分函数返回基准最终位置p,然后递归[left, p-1]和[p+1, right],这样不会把基准重复纳入。
def quick_sort(arr, left, right): if left >= right: return pivot = arr[left] # 取最左为基准 i, j = left, right while i < j: while i < j and arr[j] >= pivot: # 右指针找比基准小的 j -= 1 arr[i] = arr[j] while i < j and arr[i] <= pivot: # 左指针找比基准大的 i += 1 arr[j] = arr[i] arr[i] = pivot # 基准归位 quick_sort(arr, left, i-1) quick_sort(arr, i+1, right)这段代码里i < j是硬约束,漏掉就会越界;arr[i] = arr[j]的赋值顺序决定了基准最后落在i位置。参数left、right是闭区间,递归时排除基准本身。面试如果让你比较快排和归并,答“快排原地但不稳定、归并稳定但要多 O(n) 空间”即可,不要展开成论文。
3.2 二分查找:循环条件和 mid 取整是两大失分点
二分查找在文档里对应“二分搜寻法”,还配了插补搜寻和费氏搜寻做对比。真题里最高频的问法是“手写一个二分,注意什么”。答题模板:left = 0, right = n - 1,循环条件left <= right,mid = left + (right - left) / 2防止溢出,找到返回下标,arr[mid] < target则left = mid + 1,否则right = mid - 1。三个细节必须主动说:循环条件是<=不是<;mid 用减法写法防整型溢出;找不到时返回-1或插入位置要提前约定。插补搜寻适合数据分布均匀的场景,按比例估算 mid;费氏搜寻用斐波那契数分割,适合数据量大且不便乘除的环境。这三者的对比常以选择题出现,记住“均匀分布用插补、避免乘法用费氏”就能选对。
3.3 排序算法对比表:背这张表比背代码快
| 算法 | 平均时间 | 最坏时间 | 空间 | 稳定性 |
|---|---|---|---|---|
| 冒泡/插入/选择 | O(n²) | O(n²) | O(1) | 冒泡、插入稳定,选择不稳定 |
| Shell 排序 | O(n^1.3) 左右 | O(n²) | O(1) | 不稳定 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 |
| 合并排序 | O(n log n) | O(n log n) | O(n) | 稳定 |
| 基数排序 | O(d(n+r)) | O(d(n+r)) | O(n+r) | 稳定 |
这张表在选择题和面试口述里都直接可用。文档里还收了 Shaker 排序(双向冒泡)和改良选择排序,考的频率低,但被问到“冒泡还能怎么改”时,答“双向扫描、每轮同时把最大和最小归位”就够了。
4. 数论与大数运算:筛法、GCD 和大数加减的固定套路
4.1 Eratosthenes 筛法:从 2 开始划掉倍数
求质数的题,答题先给复杂度:筛法 O(n log log n),比逐个试除的 O(n√n) 快一个量级。步骤是开一个布尔数组全部置真,从 2 开始,如果当前数没被划掉就是质数,然后把它的所有倍数划掉,循环到 √n 即可。易错点是数组大小要开到n+1,以及内层循环从i*i开始而不是2*i,后者会做大量重复劳动。文档里“Eratosthenes 筛选求质数”就是这一套,面试追问“内存不够怎么办”时,答“分段筛”即可,不必展开。
4.2 最大公因数与最小公倍数:辗转相除一句话说清
GCD 用辗转相除:gcd(a,b) = gcd(b, a % b),b == 0时返回a。LCM 等于a / gcd(a,b) * b,先除后乘是为了防溢出,这个顺序面试官很爱抓。文档里还把因式分解、完美数、阿姆斯壮数放在同一组,这类题的共同套路是“枚举因子到 √n,成对记录”。完美数判断就是因子和等于自身,6 和 28 是最常见的两个例子,答题时能报出这两个数就够用。
4.3 大数运算:用数组模拟竖式,进位单独处理
超长整数运算的答题框架:用数组或字符串存每一位,低位在前方便对齐;加法从低位逐位相加,carry记录进位;减法要先比较大小、借位处理;乘法是双重循环result[i+j] += a[i] * b[j],最后统一进位。文档里“长 PI”和“大数运算”都属于这一类。面试常问“为什么不用现成的大数库”,标准答法是“考察对进位和边界的手动控制能力”。写的时候注意最高位进位不要丢,这是血泪经验里最常见的一处翻车。
5. 易错点复盘:这五类题丢分最多,逐条对照改
5.1 递归题忘记回退状态
错在哪:迷宫、八皇后、排列组合里,递归返回后没有把标记改回去。为什么错:全局数组被上一次分支污染,后面的分支会误判“已访问”。怎么答对:在递归调用之后立刻写回退语句,并在口述时主动说“这里要恢复现场”,面试官听到这句基本就放心了。
5.2 三色棋的指针边界写反
错在哪:把bFlag、wFlag、rFlag的移动条件记混,导致死循环或漏元素。为什么错:这三个指针代表的是“蓝区右边界、未处理区左边界、红区左边界”,不是颜色计数。怎么答对:记住规则——遇到白色wFlag++;遇到蓝色与bFlag交换后两者都加;遇到红色与rFlag交换后rFlag--,循环条件是wFlag <= rFlag。
5.3 背包问题把 0-1 背包和完全背包混写
错在哪:一维数组优化时,0-1 背包内层循环正序,完全背包也正序。为什么错:0-1 背包每件物品只能用一次,内层必须倒序才能保证用的是上一轮状态;完全背包可重复取,才用正序。怎么答对:先判断物品数量限制,再决定循环方向,答题时把这句“倒序防重复、正序允许重复”说出来。
5.4 约瑟夫问题用模拟却算错下标
错在哪:报数到 m 出列后,下一轮起点算错。为什么错:数组删除元素后下标整体前移,直接i = (i + m - 1) % n再删会偏移。怎么答对:用链表或列表模拟时,出列后当前位置就是下一个起点,不要再加一;数学解法记住递推式f(n) = (f(n-1) + m) % n。
5.5 巴斯卡三角形组合数溢出
错在哪:用阶乘直接算C(n,r),n 稍大就溢出。为什么错:阶乘增长远快于组合数本身。怎么答对:用递推C(n,r) = C(n-1,r-1) + C(n-1,r)逐行生成,或者像文档里那样边乘边除p = p * (n-i+1) / i,保证中间结果可控。
6. 考前怎么用这份题库:三轮刷题法和口述自检
拿到 51 个例子,不要从第一页顺着看到最后一页,那样看完就忘。我的做法是分三轮。第一轮按模块扫,每道题只做一件事:合上答案,用三句话说出“这题考什么结构、终止条件是什么、复杂度多少”。说不出来的做记号,这一轮目标是建立考点地图,不是写代码。第二轮只攻做记号的题,每道题手写一遍核心函数,写完对照文档源码,重点看边界条件和回退语句,写错的在旁边标“翻车点”。第三轮做口述模拟,随机抽题,假装面试官在对面,要求自己在两分钟内讲清思路、再花三分钟写出关键代码,讲的时候刻意带上“这里要注意”“这类题通常这么答”这类过渡句,练的是表达节奏而不是背诵。
下面这张自检表可以贴在桌前,每刷完一题打一个勾:
| 自检项 | 达标标准 |
|---|---|
| 能否说出考点归属 | 递归/排序/数论/矩阵,一秒归类 |
| 能否写出终止条件 | 不看书直接写,且覆盖边界 |
| 能否说清复杂度 | 时间、空间各一句,不含糊 |
| 能否指出一个易错点 | 具体到某一行或某个下标 |
| 能否口述两分钟 | 不卡壳、不跑题、有结论 |
这份资料的价值不在于 51 这个数字,而在于它把每类题的“标准解法 + 可运行源码”放在一起,省去了你在网上东拼西凑的时间。文档是 Word 格式、126 页,题目、思路、分析和源码都在一处,适合打印出来做第二轮的手写对照。需要拿去练的,按文末方式获取即可。
从那以后我每次准备这类算法考核,都强制自己先口述再动笔,讲不顺的地方就是没真懂的地方,回头补那一块,比盲目刷十道新题管用。希望帮到你。
本文还有配套的精品资源,点击获取