DFS必须配visited map以防环导致无限递归,因图可能存在A→B→A等环路,而树天然无环可省略;邻接表表示图时必须加visited,推荐map[int]bool支持非连续ID,连续ID可用[]bool提升性能。

Go 里写 DFS,递归最直接,但得防栈溢出;图必须用 visited 标记,漏了就死循环。
DFS 为什么必须配 visited map?
图不是树,可能有环或双向边。不标记访问状态,DFS 会反复跳回已处理节点,比如 A→B→A→B…无限递归下去。
- 树结构天然无环,可省
visited;一旦用邻接表表示成图(哪怕只是树转图),就得加 - 用
map[int]bool最灵活,支持非连续节点 ID;若节点 ID 是0到n-1,用[]bool更快、无哈希开销 - 别在递归入口只判
if visited[node] { return }—— 必须在标记后立刻递归子节点,否则标记失效
递归 DFS 和手写栈 DFS 怎么选?
Go 默认 goroutine 栈只有 2KB,深度超过千级的图用递归容易爆栈。
- 适合递归:二叉树遍历、小规模图(节点
- 必须手写栈:链状图、社交网络长路径、明确含超深分支的场景;用
stack := []*Node{start}+pop := stack[len(stack)-1]; stack = stack[:len(stack)-1] - 无论哪种,
visited都要在 pop 后立即设为true,不能等到进循环才标
邻接表用 map[int][]int 还是 []([]int)?
绝大多数情况选 map[int][]int,尤其节点 ID 不连续或动态增删时。
立即学习“go语言免费学习笔记(深入)”;
-
map[int][]int:支持任意整数 ID,增删边方便,代码不易错;小图性能损失可忽略 -
[]([]int):仅当节点 ID 严格是0~n-1且n已知,空间和访问都更高效;但g.adj[u]可能 panic,得先make容量 - 无向图记得双向加边:
g.adj[u] = append(g.adj[u], v)和g.adj[v] = append(g.adj[v], u);漏一边,图就不连通
DFS 返回路径时怎么避免切片共享问题?
Go 中切片是引用类型,直接传 path 会导致多层递归共用底层数组,结果错乱。
- 正确做法:传指针
*[]int或每次递归前append(path, node)得到新切片再传入 - 回溯时不用“清空”,而是靠函数调用栈自然退出;若要复用
path,需在递归返回后path = path[:len(path)-1] - 找一条路径就返回?用
return true控制提前退出;找所有路径?用闭包或额外参数收集结果
真正麻烦的不是写法,而是状态管理——visited 标在哪、path 拷不拷、栈用不用手动维护,每个点都卡在实际跑起来才暴露。写完别急着测逻辑,先用三节点带环图跑一遍,看会不会卡住。


















