GESP 六级 第 05 讲:深度优先搜索 DFS

本讲定位:编程题第 2 题的主战场。近三次真题的第 2 题全是树上 DFS:2026 年 6 月「满二叉树子树计数」、2026 年 3 月「完全二叉树子树计数」、2025 年 12 月「路径覆盖」。25 分,题型高度稳定。

六级第 03 讲已经在二叉树上把「后序汇总」这个模板建起来了。本讲做三件事:把 DFS 讲成一个通用方法(不局限于二叉树)、推广到一般树(孩子不止两个,输入是父亲数组)、把它用在带决策的问题上(树形 DP)。另外补上大纲里的网格 DFS 与回溯。

本讲建议安排两周。

对应官方大纲:六级知识块 3「搜索算法」——深度优先搜索算法(DFS)。

前置知识:六级第 02 讲(遍历)、六级第 03 讲(子树统计模板)、五级第 06 讲(递归、记忆化、剪枝)。

代码说明:本讲代码均已用 g++ 11.5(-std=c++11)编译运行验证;两道真题程序在官方样例上输出一致,路径覆盖另与 $2^n$ 枚举暴力对拍。


1. DFS 是什么

$$ \boxed{\textbf{一条路走到底,走不动了就退回上一个岔路口,换一条继续}} $$

这就是「深度优先」:优先往深处走,而不是把身边的都先看一遍(那是下一讲的 BFS)。

前面其实一直在用它:

你写过的 其实就是 DFS
二叉树的前/中/后序遍历(六级第 02 讲) 在二叉树上的 DFS
递归求高度、数叶子(第 02、03 讲) DFS + 回来时汇总
满二叉树子树计数(六级第 03 讲) DFS + 后序汇总

所以本讲不是从零开始,是给已经在用的东西起个名字、理清套路。

1.1 递归 = 系统帮你维护的栈

五级第 06 讲讲过:每次递归调用,系统在调用栈上压一层。DFS 的「退回上一个岔路口」,靠的正是弹栈。

也可以自己拿一个栈来写(六级第 02 讲第 3.4 节的非递归前序就是):

写法 优点 缺点
递归 短、直观 深度太大会栈溢出
显式栈 不怕深 代码长,容易写错

⚠️ 判断题常客(2026 年 3 月六级单选第 13 题):「对一棵树进行 DFS 时,常借助递归或栈实现」——正确。

同一题的三个错误选项也要会判:

1.2 递归深度的红线

沿用五级第 06 讲的口径:

$$ \boxed{\text{递归深度} \le 10^5 \text{ 可以放心;} \ge 10^6 \text{ 必须改写成非递归}} $$

六级编程题的 $n$ 通常是 $10^5$,最坏情况(树退化成一条链)深度正好 $10^5$,能过。这一点要让学生安心,不要一看到 $10^5$ 就去写显式栈。


2. 后序汇总模板(回顾并固化)