CalcGuide · 技术博客主页 / 一页纸学习计划
🔥极高

递归与回溯:从递归树到剪枝

分类:数据结构与算法 · 路径:docs/topics/recursion-and-backtracking/README.md

#algorithms#recursion#backtracking#pruning#python

理解递归调用栈、终止条件、回溯模板(排列 / 组合 / 子集),能写剪枝

父主题

算法设计方法:从递归到动态规划

子主题(0)

递归与回溯:从递归树到剪枝

0. 元信息

1. 学习路线

递归调用栈与终止条件
  → 数学归纳与递归正确性
  → 回溯模板(选 / 不选 + 撤销)
  → 排列 / 组合 / 子集三连
  → 剪枝(行级、对角线、上界)
  → N 皇后 / 数独 / 单词搜索

每一步都跟一道题。先把递归树画出来,再写代码。

2. 阶段周数分配

阶段1.5 周方案2 周方案备注
1. 递归基础0.5 天1 天调用栈 + 终止条件 + 5 道入门
2. 回溯模板1.5 天2 天46 / 78 / 77 三连 + 撤销
3. 剪枝基础1 天1.5 天39 / 40 / 47 去重 + 上界
4. 字符串回溯1 天1.5 天22 括号 + 79 单词搜索
5. 难题剪枝1.5 天2 天51 N 皇后 + 37 数独
6. 复盘 + 综合题0.5 天1 天140 单词拆分 + 错题重做

每天 1.5~2 小时。1.5 周方案专注前 5 阶段;2 周方案多 2 天做复盘与错题重写。

3. 九阶段表(精简)

3.1 核心知识

3.2 实践产出

3.3 可观察学会标准

4. 第一周(每天 1.5~2 小时)

Day 1 约定:本计划使用 Python 3.10+。统一格式:python -X dev -W error -m py_compile foo.py 做静态检查;运行用 python foo.py < input.txt;测试用 pytest。所有代码放进 algo-design/recursion/week<N>/ 目录;每个题目单独 .py 文件,配套 notes/<problem>.md 含递归树图与复杂度推导。

任务当天交付自检
Day 1装 Python 3.11 + pytest + ruff;手写 5 道递归(阶乘、斐波那契、阶乘和、汉诺塔、递归求和);为每道画递归调用栈图week1/day1/recursion.md 含 5 张调用栈图pytest week1/day1/test_recursion.py 5 passed;python -c "import sys; sys.setrecursionlimit(200000); print(fib(900))" 跑通
Day 26 道回溯模板:46 全排列、78 子集、77 组合、22 括号生成、39 组合总和、79 单词搜索week1/day2/backtrack.py + 提交记录pytest week1/day2/test_backtrack.py 6 passed;LeetCode 提交 6/6 AC;每题记录分支因子
Day 34 道去重回溯:47 全排列 II、40 组合总和 II、90 子集 II、491 递增子序列week1/day3/backtrack_dedup.pypytest week1/day3/test_backtrack_dedup.py 4 passed;LeetCode 4/4 AC;记录去重条件 if i > start and nums[i] == nums[i-1]: continue
Day 4字符串 / 网格回溯:79 单词搜索(网格)、212 单词搜索 II(字典树 + 回溯)、140 单词拆分 IIweek1/day4/grid_backtrack.pypytest week1/day4/test_grid.py 3 passed;LeetCode 3/3 AC;记录 visited[i][j] 防回头
Day 5剪枝难题(上界剪枝):216 组合总和 III、377 组合总和 IV、40 组合总和 II 上界week1/day5/pruning_upper.pypytest week1/day5/test_pruning.py 3 passed;对比加/不加上界剪枝的运行时间(n=20 时差距 >10x)
Day 6N 皇后:51 N 皇后(行级 + 列级 + 主对角线 + 副对角线四种剪枝);用三个 set 维护约束week1/day6/n_queens.py + 递归树图pytest week1/day6/test_n_queens.py 4 passed(n=4, 6, 8, 10);n=10 在 < 5s 内跑完
Day 7步骤 A:完成 N 皇后(n=8 → 92 解)的完整推导 + 实现 + 时间记录;步骤 B:补齐 4 类边界(n=0 / n=1 / n=2 无解 / n=10 全解)week1/day7/n_queens.py + 测试日志pytest -k test_n_queens 4 个用例全过;每类边界用例的输入/期望/实际写到 notes/week1-day7.md

Day 7 执行次序

步骤 A —— N 皇后完整版(60~90 分钟)

  1. solve_n_queens(n) 返回所有解;
  2. 画 n=4 的递归树(带剪枝后);
  3. 跑 n=8 输出 92 解;
  4. 记录 n=4, 6, 8, 10 的耗时。

步骤 B —— 4 类边界用例(30~45 分钟)

#用例期望行为验证命令
B1n=0返回 [[]](空棋盘)pytest -k test_n_zero
B2n=1返回 [[1 解]]pytest -k test_n_one
B3n=2, n=3返回 [](无解)pytest -k test_no_solution
B4n=10返回 724 解,< 5spytest -k test_n_ten

Day 7 当天必完成步骤 A;步骤 B 至少完成 B1、B3。

5. 阶段通用验收(精简)

  1. 不看答案重写 8 道回溯模板;
  2. 用自己的话解释递归调用栈每一步发生什么;
  3. 画出 N 皇后的递归树(含剪枝后);
  4. 测试 n=0、n=1、n=4、n=8、n=10 时的运行时间和分支数;
  5. 至少准备 3 组自定义数据贴出实际输出;
  6. 记录每题的时间空间复杂度;
  7. 能给现有回溯加新剪枝条件。

6. 最终验收

7. 综合项目

首选:回溯算法可视化工具(必做:CLI 输入题目 + 输出回放 + 可选 GUI)。
备选:N 皇后求解器 + 递归树导出(输入 n → 输出所有解 + 递归树 DOT 文件 + Graphviz 渲染)。
备选:回溯题模板生成器(给定题型关键词 → 输出 Python 模板:选 / 不选 + 撤销 + 剪枝条件)。

回溯算法可视化工具必做要求:

任何综合项目都必须包含:

  1. 需求说明(含输入输出约定);
  2. 数据结构与算法选择理由(为何用递归 + 回溯、为何选这种剪枝);
  3. 核心模块说明(Solver / Recorder / Renderer 三层);
  4. 模块化源码(每题单独文件 + 公共 base class);
  5. 边界测试(n=0, 1, 2, 8, 10 + 网格空 + 网格满);
  6. 运行说明(Makefile 或清晰的 python -m 命令);
  7. README(项目介绍、运行步骤、目录结构、复盘);
  8. notes/ 规范
文件内容
notes/design.md数据结构选择理由、模块图、剪枝策略对比
notes/test.md每组测试数据的输入 / 期望输出 / 实际输出 / 通过情况
notes/retrospective.md用时、难点、收获、下一步
notes/recursion-trees/至少 3 题的递归树图(PNG / DOT 文件)
notes/stack-traces/关键测试的栈帧表(手画 + 程序输出)
notes/pruning-comparison.md加 / 不加剪枝的耗时对比表
notes/edge-cases.md4 类边界用例的输入 / 期望 / 实际
notes/algorithm-templates/4 个回溯模板的 Python 模板代码

本主题贡献

3 职责

  1. 把”问题可分解性”显式化为递归栈帧表 —— 每层栈帧保存什么(参数 + 局部变量 + 返回地址)、什么时候压栈 / 弹栈、RecursionError 的触发边界(默认 1000 层,setrecursionlimit(200000) 后才能稳定跑 N=10 的 N 皇后)。
  2. 给出可移植的”选 → 递归 → 撤销”回溯模板 —— path + used + start 三件套;并能针对子集树(if i > start and nums[i] == nums[i-1]: continue 去重)和排列树(used[i] 标记已访问)切换不同的去重条件与剪枝触发点。
  3. 用可视化验证递归树与剪枝收益 —— 行级 / 列级 / 主对角线 col - row / 副对角线 col + row 四种剪枝在 N 皇后上对比”加 / 不加”耗时差异(n=20 时应 ≥ 10x),并把递归栈快照存到 notes/stack-traces/

4 交付物

  1. 19 道题解(8 入门递归 + 8 回溯模板 + 3 剪枝难题)+ 4 类边界用例日志(n=0 返回 [[]] / n=1 单解 / n=2 / n=3 无解 / n=10 全解)。
  2. 回溯算法可视化工具(CLI → 算法名 + 数据集 → 输出文字回放 + 递归栈每一步快照 + 解集总数)+ 至少 3 题的子集树 / 排列树 DOT 文件。
  3. 4 个回溯模板的 Python 代码(46 全排列 / 78 子集 / 77 组合 / 39 组合总和)落到 notes/algorithm-templates/,每个模板独立标注”撤销时机”与”去重条件”。
  4. 剪枝收益对比表 notes/pruning-comparison.md(行级、列级、主对角线、副对角线、上界剪枝五类的耗时 ×10 差距证据)。

3 指标

  1. 不看答案 25 分钟内独立完成 46 / 78 / 77 三连(覆盖”选 / 不选 + 撤销”模板的全部变体与去重边界)。
  2. N 皇后 n=10 在 < 5s 内跑完 724 解(验证四种剪枝叠加后的实际收益,证明 Word Search / 数独 / N 皇后剪枝套路已可迁移)。
  3. 任意一道回溯题 5 分钟内画完递归树 + 标出分支因子(说明递归树的”形状分析”已固化为肌肉记忆,能在编码前估时间复杂度)。
阶段角色资料链接用法
1~2入门书Manber《Introduction to Algorithms: A Creative Approach》https://www.cs.arizona.edu/~merlin/Algorithms.pdf用归纳讲递归,必读第 1~2 章
1~2教材CLRS《Introduction to Algorithms》第 4 版第 4 章https://mitpress.mit.edu/9780262033848/分治与递归基础
2范式Skiena《The Algorithm Design Manual》第 8 章https://www.algorist.com/Backtracking 范式 + 实战题
2~5框架labuladong 回溯框架https://labuladong.online/algo/中文框架化讲解;先自己写再对照
2~5图解CP-Algorithms Backtrackinghttps://cp-algorithms.com/英文图解 + 代码;当字典
2~5刷题LeetCodehttps://leetcode.cn/中文题库;回溯题按 tag 刷
2~5刷题LeetCode Discuss · 回溯https://leetcode.com/discuss/题目分类讨论;看官方 Editorial
3可视化Algorithm Visualizerhttps://github.com/algorithm-visualizer/algorithm-visualizer递归 / 回溯算法可视化
3可视化VisuAlgohttps://visualgo.net/回溯与递归树可视化
4~5题目AtCoder Beginner Contesthttps://atcoder.jp/ABC C/D 题含回溯
4~5题目USACO Guidehttps://usaco.guide/美国队训练;按难度递进
全部人物Knuth TAOCP Vol.4A 组合搜索https://www-cs-faculty.stanford.edu/~knuth/taocp.html当字典;遇到细节再查

许可证提示:Manber 自学合理引用;CP-Algorithms 是 CC BY-SA;LeetCode 题面版权见 LeetCode Terms of Service——代码自己写,思路可以分享;AtCoder / USACO Guide 题面公开。默认做法是读思路后自己写代码,而不是复制 Editorial 代码。

默认使用顺序:先用 labuladong 回溯框架建立模板感 → 自己写 46 / 78 / 77 三连 → 对照 CP-Algorithms 看反例与去重 → 用 LeetCode 刷 12 道(按 tag) → 跑 51 N 皇后 4 种剪枝 → 用 Algorithm Visualizer / VisuAlgo 验证递归树 → 选 Manber 第 1~2 章做归纳思维训练 → 写复盘到 notes/retrospective.md

8. 推荐开源资料

阶段角色资料链接用法
1~2入门书Manber《Introduction to Algorithms: A Creative Approach》https://www.cs.arizona.edu/~merlin/Algorithms.pdf用归纳讲递归,必读第 1~2 章
1~2教材CLRS《Introduction to Algorithms》第 4 版第 4 章https://mitpress.mit.edu/9780262033848/分治与递归基础
2范式Skiena《The Algorithm Design Manual》第 8 章https://www.algorist.com/Backtracking 范式 + 实战题
2~5框架labuladong 回溯框架https://labuladong.online/algo/中文框架化讲解;先自己写再对照
2~5图解CP-Algorithms Backtrackinghttps://cp-algorithms.com/英文图解 + 代码;当字典
2~5刷题LeetCodehttps://leetcode.cn/中文题库;回溯题按 tag 刷
2~5刷题LeetCode Discuss · 回溯https://leetcode.com/discuss/题目分类讨论;看官方 Editorial
3可视化Algorithm Visualizerhttps://github.com/algorithm-visualizer/algorithm-visualizer递归 / 回溯算法可视化
3可视化VisuAlgohttps://visualgo.net/回溯与递归树可视化
4~5题目AtCoder Beginner Contesthttps://atcoder.jp/ABC C/D 题含回溯
4~5题目USACO Guidehttps://usaco.guide/美国队训练;按难度递进
全部人物Knuth TAOCP Vol.4A 组合搜索https://www-cs-faculty.stanford.edu/~knuth/taocp.html当字典;遇到细节再查

许可证提示:Manber 自学合理引用;CP-Algorithms 是 CC BY-SA;LeetCode 题面版权见 LeetCode Terms of Service——代码自己写,思路可以分享;AtCoder / USACO Guide 题面公开。默认做法是读思路后自己写代码,而不是复制 Editorial 代码。

默认使用顺序:先用 labuladong 回溯框架建立模板感 → 自己写 46 / 78 / 77 三连 → 对照 CP-Algorithms 看反例与去重 → 用 LeetCode 刷 12 道(按 tag) → 跑 51 N 皇后 4 种剪枝 → 用 Algorithm Visualizer / VisuAlgo 验证递归树 → 选 Manber 第 1~2 章做归纳思维训练 → 写复盘到 notes/retrospective.md

9. 学习资料汇聚(v0.3)

9.1 背景与动机

递归诞生于 1930 年代 lambda 演算与可计算性研究。McCarthy 在 1958 年的 LISP 中把递归作为核心控制结构。1970 年代 Knuth 在 TAOCP 把回溯作为组合搜索的标准方法。N 皇后是 1848 年由 Max Bezzel 提出、1970 年代成为回溯教学题。

行业位置:回溯是中等难度面试常考题(LeetCode 22 / 39 / 46 / 47 / 78 / 79);竞赛里是 DFS 搜状态空间的核心。不会递归与回溯,后续 DP / 分治都没法谈。一句话总结:递归是算法的递归,DP 是带记忆的递归,分治是互不重叠的递归。

9.2 概念地图

flowchart LR
  CallStack[调用栈]
  BaseCase[终止条件]
  Induction[数学归纳]
  Backtrack[回溯模板]
  Permutation[排列]
  Combination[组合]
  Subset[子集]
  Pruning[剪枝]
  NQueens[N 皇后]
  Sudoku[数独]
  WordSearch[单词搜索]
  CallStack --> BaseCase
  BaseCase --> Induction
  Induction --> Backtrack
  Backtrack --> Permutation
  Backtrack --> Combination
  Backtrack --> Subset
  Backtrack --> Pruning
  Pruning --> NQueens
  Pruning --> Sudoku
  Backtrack --> WordSearch

核心关系:调用栈是底座 → 终止条件防爆栈 → 数学归纳证明正确性 → 回溯模板三连 → 剪枝去掉不可能分支。

9.3 基础知识讲解

9.3.1 经典论文

资料影响建议读法
McCarthy, Recursive Functions of Symbolic Expressions(1959/1960)LISP 与递归作为一等公民了解递归在编程语言里的地位
Floyd, Nondeterministic Algorithms(1967)回溯搜索的形式化看 nondeterministic choice + backtrack
Knuth & Moore, An Analysis of Alpha-Beta Pruning(1975)博弈树的剪枝分析把剪枝思想讲透
Bezzel 1848 N 皇后原始问题回溯教学起点看历史意义,代码自己写

9.3.2 经典书籍

影响用法
CLRS Introduction to Algorithms 第 4 版递归与分治章节精读第 4 章”Divide-and-Conquer”
Skiena The Algorithm Design Manual 第 8 章”Backtracking”回溯范式 + 实战题看 8.1~8.5 节
Manber Introduction to Algorithms用归纳讲递归第 1~2 章当思维训练
Roughgarden Algorithms Illuminated 第 1 册直觉建立第 3~4 章当入门

9.3.3 优秀博客

资料特点用法
labuladong 回溯框架中文框架化讲解当 Cheatsheet;先自己写再对照
CP-Algorithms Backtracking英文图解 + 代码当字典
LeetCode Discuss · 回溯题目分类讨论看官方 Editorial 优先
Codeforces Blog · Petr Mitrichev高级竞赛视角当远期目标

9.3.4 核心人物

人物主要影响建议追踪的材料
John McCarthyLISP + 递归作为一等公民1960 CACM 论文
Robert FloydFloyd-Warshall + 回溯形式化1967 Nondeterministic Algorithms
Donald KnuthTAOCP 组合搜索章节Vol.4A fascicles
Hua Luogeng(华罗庚)统筹方法与优选法与算法无关,但启发”剪枝”思想
Petr Mitrichev竞赛圈 Editorial 大神Codeforces 博客

9.3.5 开发方法

方法具体动作何时用
Recursion-tree first先画递归树,再写代码任何递归题
Math induction framing把”算法正确性”写成数学归纳验证正确性
Pruning-from-leaves从叶子反推哪些分支不该走N 皇后 / 数独
Path copy vs singlepath[:] 还是 path.pop() 撤销回溯题
Order matters排列 / 组合 / 子集的去重方式47 / 40 / 90 题

9.4 经典问题与经典案例

#问题为什么重要最简答案
1阶乘 / 斐波那契 / 汉诺塔递归三件套;理解调用栈终止条件 + 递推
2LeetCode 46 全排列回溯模板第一题for i in range(n): if not used[i]: path.append(nums[i]); used[i]=True; dfs(); path.pop(); used[i]=False
3LeetCode 78 子集组合 vs 子集区别选/不选二选一
4LeetCode 77 组合顺序去重start 参数控制起点
5LeetCode 39 / 40 组合总和可重复 / 不可重复 + 数组去重40 题加 if i > start and nums[i] == nums[i-1]: continue
6LeetCode 22 括号生成字符串回溯left < n 才能放 (,left > right 才能放 )
7LeetCode 51 N 皇后剪枝极致用三个 set 维护列 + 主对角线 + 副对角线
8LeetCode 37 数独解二维回溯行 + 列 + 宫三种约束
9LeetCode 79 单词搜索网格回溯visited[i][j] 防回头,四个方向 DFS
10LeetCode 140 单词拆分 II回溯 + 字典匹配单词拆分 II 是 NP-hard,需要 memo

9.5 学习难点

概念难点

难点为什么会卡突破路径
调用栈帧看不到函数怎么压栈弹栈手画 n=3 时汉诺塔的栈帧表
终止条件放错位置多走或少走一步写一个最小例子 n=1, n=2 看递归树
选/不选 vs for 循环子集 vs 排列模板搞混把 78 / 46 / 77 并排看
撤销忘了path 残留严格模板:选 → 递归 → 撤销

思维难点

难点为什么会卡突破路径
递归树太深画不下只画两层,把第 3 层用虚线表示
估算分支因子不知道时间复杂度数每个节点的子节点数;用叶子数 × 每节点代价
剪枝条件不充分TLE 或 WA把约束列出来,每加一条剪枝测一次

工程难点

难点为什么会卡突破路径
Python 递归超 1000RecursionErrorsys.setrecursionlimit(200000);或改写迭代
字符串拼接性能差path + char 是 O(n)path.append(char) + path.pop()
全局变量 vs 参数传递状态污染优先用参数;用 nonlocal 时小心
重复子问题回溯 TLE加 memo;或换 DP

9.6 技术标准与接口

9.6.1 Entity

名称版本 / 文档发布组织状态许可证 / 可访问性
Python sys.setrecursionlimitCPython 3.10+Python Software Foundation内建PSF License
functools.lru_cacheCPython 3.10+PSF内建PSF License
LeetCode 回溯题库持续更新LeetCode订阅制ToS 管理
CP-Algorithms Backtracking持续更新社区活跃CC BY-SA

9.6.2 Scope

9.6.3 Structure

9.6.4 Ecosystem

9.6.5 Depth Tiers

层级能力递归与回溯的可观察标准
L0知道存在知道递归与回溯是组合搜索的标准方法
L1看得懂示例能读懂别人的回溯代码
L2能正确调用能写出 46 / 78 / 77 三连
L3能解释与排错能画递归树、能加剪枝、能解释 RecursionError 与撤销
L4能设计与扩展能把”网格回溯”或”博弈树”扩展到新题型

本子主题目标:L3。N 皇后 / 数独 / 单词搜索做对即达到。

9.6.6 Source

10. 常见误区


11. 所有知识点分类

  1. 编程语言(辅):Python 列表 / 字典、path.append + path.pop 撤销、sys.setrecursionlimitfunctools.lru_cacheitertools.permutations / combinations 验证。
  2. 数据结构与算法(主):调用栈与栈帧、终止条件与数学归纳、回溯模板(选 / 不选 + 撤销)、排列 / 组合 / 子集、剪枝(行级 / 列级 / 对角线 / 上界)、N 皇后 / 数独 / 单词搜索 / 括号生成。
  3. 计算机基础:栈内存模型、过程调用与返回地址、递归的数学归纳证明、排列组合计数。
  4. 工程技术:复杂度分析(分支因子 × 叶子数)、画递归树、撤销与状态恢复、避免全局变量污染、剪枝验证流程。
  5. Web 与后端:无直接关联。
  6. 前端与客户端:无直接关联。
  7. 数据与人工智能:无直接关联(回溯可作为搜索子模块,但本计划不展开)。
  8. 项目与职业能力:LeetCode 22 / 39 / 46 / 47 / 51 / 78 / 79 等高频面试题的解题节奏、25 分钟独立完成模板题、5 分钟画递归树并标分支因子。

本计划归属:数据结构与算法 主 + 编程语言 辅。


直接依赖(0)

查看知识图谱 · 热度 🔥极高