dfs递归易栈溢出,因系统栈空间有限,节点过万或邻接表未去重时尤其危险;需加visited数组、传const引用、检查空图,并在深度超10⁴时切迭代。

DFS递归实现:为什么栈溢出比你想象中来得快
递归写法最直观,但实际项目里容易在深图或环图上崩掉。核心问题不是逻辑错,而是系统栈空间耗尽——尤其当节点数过万、邻接表没做去重时,std::stack 用得多,call stack 更吃紧。
- 必须加访问标记数组(
vector<bool> visited</bool>),否则自环或无向边会无限递归 - 邻接表传参建议用
const vector<vector>>& graph</vector>,避免拷贝开销 - 递归入口别直接从 0 开始硬写,先检查
graph.size() > 0,空图不调用 - Windows 下默认栈只有 1MB,Linux 可能更小;超 10⁴ 层深度就该切迭代
示例片段:
void dfs(int u, const vector<vector<int>>& graph, vector<bool>& visited) {
visited[u] = true;
for (int v : graph[u]) {
if (!visited[v]) dfs(v, graph, visited);
}
}
DFS迭代实现:手动栈里藏着三个关键状态
迭代不是简单把递归函数体塞进 while 循环。真正难点在于:递归隐式维护了「当前节点」+「未处理的邻接点索引」,而手动栈必须显式存这两样,否则会漏边或重复访问。
- 推荐用
stack<pair int>></pair>:first 是节点编号,second 是它在邻接表中的下一个待查下标 - 每次 pop 后,先标记访问,再从
graph[u][idx]开始遍历,把后续未访问邻居连同新 idx 压栈 - 不能只压节点(
stack<int></int>),否则无法知道“这个节点还有哪几个邻居没看” - 初始化时每个起点要压入
{start, 0},不是{start, -1}—— 下标从 0 开始
无向图 vs 有向图:邻接表构造时最容易漏掉的边方向
DFS 本身不区分有向无向,但建图错了,遍历结果就全偏了。常见错误是读入无向边却只加单向边,导致一半节点根本进不来。
- 无向图:每条输入边
u-v必须同时执行graph[u].push_back(v)和graph[v].push_back(u) - 有向图:只加
graph[u].push_back(v),反向边不存在 - 混合图?别手写,用
struct Edge { int to; bool is_directed; };加权或带属性时更要小心 - 调试技巧:打印
graph[i].size()看各节点度数是否符合预期
vector 的坑:它不是真正的容器
用 vector<bool></bool> 做访问标记很常见,但它其实是位压缩特化类,不支持取地址、迭代器行为异常,某些编译器下 visited[u] = true 可能不生效。
立即学习“C++免费学习笔记(深入)”;
- 生产环境优先用
vector<char></char>或vector<int></int>,内存多占不到 1%,但行为确定 - 如果坚持用
vector<bool></bool>,确保编译器版本 ≥ GCC 11 或 Clang 14,旧版有已知优化 bug - 千万别对
vector<bool></bool>做&visited[i]操作,会编译失败或运行时崩溃 - 初始化别写
vector<bool>(n, false)</bool>—— 虽然语法合法,但部分 STL 实现里默认值语义不稳
visited 类型是否可靠、以及递归深度是否真可控。这些细节一错,输出看起来像 DFS,其实只是碰巧扫到了一部分节点。










