“一个 phase 先从所有未匹配的 $L$ 顶点做多源BFS,只在交替方向上前进,首次到达未匹配右点时记录最短距离 $\ell$,继续处理所有可能到达该层的前驱,但不再向更深层扩展。层次图保…”
形式陈述
深度优先搜索(DFS)的输入是有限顶点集及可枚举的后继记录,即有向图的邻接接口;本页允许自环和平行弧,平行弧保留独立身份。无向图则把每条边存成两个反向记录。DFS 依次从尚未发现的顶点启动搜索。访问
递归调用或等价的显式栈在任一时刻保存一条 DFS 树根到当前顶点的活动路径。对白—灰—黑三色状态,白色尚未发现,灰色已发现但未完成,黑色已经完成。时间区间满足括号定理:任意两个不同顶点的区间
要么不交,要么一个严格包含另一个;在后一种情形中,外层顶点是内层顶点在 DFS 森林中的祖先。这个嵌套不变量支撑环检测、拓扑排序和许多 low-link 算法。
记原图的顶点数为
颜色、父指针、时间戳与最深调用栈共用
直觉
DFS 把“稍后继续处理的分支”留在栈帧中,先把当前分支走到底。一个帧不仅保存顶点,还保存下一条待扫描的邻接记录。递归进入白色邻点时,父帧暂停;子调用结束后,父帧从原位置继续。只有最后一条邻接记录也处理完,当前顶点才变黑并出栈。
这说明区间为什么只能嵌套或分离:发现另一个顶点时,若旧调用尚未结束,新调用必须在它返回之前结束;若旧调用已经返回,两段时间便不会重叠。邻接次序会改变树、时间戳和输出顺序,却不会改变从给定源可达的顶点集合。
这里不能把“用栈遍历”直接等同于标准 DFS。若一次性把当前点的所有邻点压栈并立即标记,就可能把本应先从某个孩子深入发现的顶点提前登记为兄弟,破坏上述祖先与时间区间的解释。要保留这些性质,显式栈应像图中的调用帧一样,每次只推进一个尚未处理的邻接项。
与按层推进的广度优先搜索相比,DFS 没有距离单调性。它换来的信息是活动路径和退出顺序:灰色顶点恰是当前递归祖先,遇到指向灰色祖先的边便得到了一个有向圈证据。
例子与边界
在有向图
中,若
按顶点
有向图中,指向白点的是树边,指向灰色祖先(包括自身)的边是回边;自环因而直接给出长度一的有向圈。指向黑点的边要结合时间区间区分前向边与交叉边。有向图无环当且仅当任意完整 DFS 森林都没有回边。无向图的非树边只连接祖先与后代,但扫描父边的反向记录不构成圈;若允许平行边,必须按边 ID 排除那一条父边,因为另一条平行边确实形成长度为二的多重图圈。
DFS 首次找到的路径一般不是最短路:在边
推论与应用
确认有向图无回边后,逆完成序可构造拓扑排序,两次 DFS 可分解强连通分量,发现时间与Low-link 值可识别桥、割点和分量。它们共享遍历骨架,正确性却分别依赖“无回边”“凝聚图次序”或“子树能回到多早”的附加不变量。
发现/完成区间还把 DFS 树的子树映成嵌套区间,树的 Euler Tour 技巧据此支持祖先或子树查询。一般有向图的非树边不会因此消失;只有先明确要查询的是 DFS 森林结构,才能把区间结论用于原图问题。
在单入口 flow graph 中,Lengauer–Tarjan 算法借 DFS 编号组织半支配点,但 DFS parent 只代表首次到达,未必支配子节点。边一旦插删,原有搜索树、时间区间和边分类都可能改变;本页的线性界只描述一次静态遍历,不是动态图维护保证。
参考资料
-
CMU 15-451/651,Lecture 8: Depth First Search and Strong Components,2019年9月19日,§§2–3:DFS 活动栈、边分类与 DAG 逆完成序。
-
Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein, Introduction to Algorithms, 4th ed., MIT Press, 2022,Ch. 20。
-
Jon Kleinberg and Éva Tardos, Algorithm Design, Pearson, 2005,§3.3。
-
Robert Sedgewick、Kevin Wayne,Algorithms,4th ed.,2011,§4.1 Undirected Graphs:递归标记、路径父指针与邻接表遍历成本。