用递归或栈实现dfs需防图不连通、有环、编号不连续:邻接表用vector时,visited大小至少为最大节点编号+1;稀疏编号宜用unordered_set;必须先校验u合法性再访问adj[u],非递归dfs需倒序入栈以模拟递归顺序。

直接用递归或栈实现 DFS 是最自然的方式,但要注意图可能不连通、有环、节点编号不连续——这些都会让简单遍历出错。
用 std::vector<:vector>></:vector> 存邻接表时怎么避免重复访问
必须维护一个 visited 数组(或 std::vector<bool></bool>),大小至少为最大可能节点编号 + 1。如果节点编号稀疏(比如只有 0、5、100 出现),用 std::unordered_set<int></int> 更安全。
- 初始化
visited全为false,进递归前设为true,出递归不重置(DFS 不回溯状态) - 别在循环里重置
visited[i] = false—— 这是 BFS 才会做的 - 如果图用 1-based 编号,
visited大小要开到n+1,否则访问visited[n]越界
dfs() 递归函数里漏写边界判断会崩
典型错误是只检查 !visited[u],却忘了 u 是否在合法范围内。尤其当输入边包含非法节点(如 -1 或 1e6)时,直接访问 adj[u] 触发 std::out_of_range 或静默越界。
- 必须先判断
u = (int)visited.size() - 邻接表
adj的索引要和visited对齐;若用unordered_map<int vector>></int>,则改用adj.count(u) && !visited[u] - 递归调用前不校验,会导致栈溢出(比如自环边
u → u且没在开头检查visited[u])
非递归 DFS 用 std::stack 时顺序和递归不一致
递归 DFS 每次取邻接表第一个未访问邻居,而用 stack 时,如果你把所有邻居一股脑 push 进去,实际遍历顺序是反的——因为栈是后进先出。
- 想严格等价递归行为:遍历
adj[u]时倒序push(即从adj[u].rbegin()到rend()) - 更稳妥做法:只
push当前节点,每次pop后再扫描邻居,遇到第一个未访问的就push并标记,然后break(模拟递归的“深度优先试探”) -
stack版本无法自然支持 DFS 树中的时间戳(如disc/low),需要额外存节点状态
真正麻烦的是处理多连通分量:必须对外层加一层循环,枚举所有可能节点,对每个 !visited[i] 才启动一次 DFS。这点容易被忽略,尤其测试用例只给单个连通块时,代码看似正确,一换数据就漏节点。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











