dfs、bfs

非常好!非递归(迭代)版本的 DFS 和 BFS 不仅避免了递归栈溢出风险,还更容易控制流程。下面分别给出 非递归 DFS 和 BFS 检测环 的完整实现(支持 无向图 和 有向图)。
✅ 一、无向图中检测环(非递归)
核心思想:
使用栈(DFS)或队列(BFS)
记录每个节点的 父节点(parent)
如果遇到一个已访问的邻居,且 不是父节点 → 有环
🔧 非递归 DFS(无向图)
include
include
using namespace std;
bool hasCycleUndirected_DFS(int n, const vector>& graph) {
vector visited(n, false);
for (int start = 0; start > stk; // (当前节点, 父节点)
stk.push({start, -1});
visited[start] = true;
while (!stk.empty()) {
auto [u, parent] = stk.top();
stk.pop();
for (int v : graph[u]) {
if (!visited[v]) {
visited[v] = true;
stk.push({v, u});
} else if (v != parent) {
return true; // 找到环!
}
}
}
}
return false;
}
⚠️ 注意:这里 pop() 后立即处理所有邻居,是标准的非递归 DFS。
🔧 非递归 BFS(无向图)
include
bool hasCycleUndirected_BFS(int n, const vector>& graph) {
vector visited(n, false);
for (int start = 0; start > q; // (当前节点, 父节点)
q.push({start, -1});
visited[start] = true;
while (!q.empty()) {
auto [u, parent] = q.front();
q.pop();
for (int v : graph[u]) {
if (!visited[v]) {
visited[v] = true;
q.push({v, u});
} else if (v != parent) {
return true; // 找到环!
}
}
}
}
return false;
}
✅ 二、有向图中检测环(非递归)
核心难点:
不能只用 visited,因为多个路径可能到达同一节点(无环)。
需要模拟 递归栈的状态 —— 即知道哪些节点“正在当前路径中”。
✅ 解决方案:使用 三色标记 + 显式栈
我们用:
color[i] = 0:未访问(白色)
color[i] = 1:在当前 DFS 路径中(灰色)
color[i] = 2:已完成(黑色)
但非递归时,我们需要手动维护“回溯”时将灰色变黑色。
🔧 非递归 DFS(有向图)—— 三色法
include
include
using namespace std;
bool hasCycleDirected_DFS(int n, const vector>& graph) {
vector color(n, 0); // 0=white, 1=gray, 2=black
for (int start = 0; start stk;
stk.push(start);
color[start] = 1; // 进入路径
while (!stk.empty()) {
int u = stk.top();
bool finished = true; // 假设 u 的所有邻居都处理完了
for (int v : graph[u]) {
if (color[v] == 0) {
color[v] = 1;
stk.push(v);
finished = false;
break; // 模拟递归:先深入一个分支
} else if (color[v] == 1) {
return true; // 遇到灰色节点 → 环!
}
}
if (finished) {
color[u] = 2; // 回溯:标记为黑色
stk.pop();
}
}
}
return false;
}
💡 关键技巧:
用 finished 标志判断是否要回溯
每次只 push 一个未访问邻居(模拟递归的深度优先)
遇到灰色节点立即返回 true
🔧 非递归 BFS(有向图)—— 拓扑排序(Kahn 算法)
这是检测有向图环的 最常用非递归方法!
#include <queue>
#include <vector>
using namespace std;
bool hasCycleDirected_BFS(int n, const vector<vector<int>>& graph) {
vector<int> indegree(n, 0);
for (int u = 0; u < n; u++) {
for (int v : graph[u]) {
indegree[v]++;
}
}
queue<int> q;
for (int i = 0; i < n; i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
int removed = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
removed++;
for (int v : graph[u]) {
if (--indegree[v] == 0) {
q.push(v);
}
}
}
return removed != n; // 如果没删完,说明有环
}
✅ 优点:简洁、高效、无需递归、天然非递归
✅ 总结:非递归环检测方法速查表
表格
图类型 方法 数据结构 是否推荐
无向图 DFS stack<pair<node, parent>> ✅
无向图 BFS queue<pair<node, parent>> ✅
有向图 DFS stack + color[3-state] ⚠️ 稍复杂
有向图 BFS 拓扑排序(Kahn) ✅✅✅(首选)
💡 建议:
无向图:用 DFS 或 BFS 都行,代码简单;
有向图:优先用 Kahn 算法(BFS 拓扑排序),非递归、稳定、易写。
你可以根据需求选择对应版本,全部都是 非递归、无栈溢出风险、可直接用于 OJ 的实现!

浙公网安备 33010602011771号