DFS递归实现必须用map[*Node]bool防环,入栈前检查访问、出栈即标记,树遍历首行须判nil,返回值需按需求设计为bool/切片/带深度参数。

DFS递归实现必须处理循环引用
Go 没有内置的图遍历函数,dfs 得自己写;最常用的是递归写法,但一不留神就会在有环图里无限递归 panic:runtime: goroutine stack exceeds 1GB limit。树结构虽无环,但若节点指针误构成环(比如双向父子引用),同样崩。
- 必须用
map[*Node]bool或map[interface{}]bool记录已访问节点,每次进入前先查表 - 别用节点值(如
int)做 key——多个节点可能值相同,但地址不同 - 对结构体指针做 map key 是安全的,Go 会比较地址,不是内容
- 如果图节点类型是自定义 struct,且不能取地址(比如作为 map value 被复制),得提前转成唯一标识(如
nodeID字段)
用栈模拟递归时注意指针和值拷贝
想避免递归栈溢出或控制深度,就得手写栈。Go 里常见错误是把节点值(而非指针)压栈:stack = append(stack, *node),这会导致后续修改丢失、重复访问同一副本。
- 栈元素类型应为
*Node,不是Node - 入栈前检查是否已访问,否则同一节点可能被多次压入(尤其邻接表未去重时)
- 出栈后立刻标记已访问,别等到处理子节点时才标——防止其他路径再次压入
- 标准库
container/list可用,但直接用切片[]*Node+append/stack[len(stack)-1]更轻量也更可控
遍历树时 nil 检查不能省
Go 的零值语义让新手容易忽略 nil 指针判断。写 dfs(root.Left) 前不检查 root == nil,运行时直接 panic:invalid memory address or nil pointer dereference。
- 递归入口第一行必须是
if root == nil { return }(或对应逻辑) - 如果树节点字段是嵌套指针(如
type Node struct { Left, Right *Node; Data *string }),Data本身也可能为nil,按需解引用 - 不要依赖 defer 捕获 panic 来兜底——DFS 属于核心逻辑,该报错就得早报错
DFS 返回值设计要匹配实际需求
很多人卡在“怎么拿到所有路径”或“找到就停”。DFS 本身不规定返回什么,但 Go 的显式返回要求你提前想清楚签名,否则中途改会造成大量重构。
立即学习“go语言免费学习笔记(深入)”;
- 只判断存在性?返回
bool,找到即return true,上层收到 true 就立刻 return,别继续递归 - 要收集所有路径?传入
*[][]int或用闭包捕获切片,注意append可能导致底层数组扩容,引发意外共享 - 找最短路径?DFS 不适合——它不保证最先找到的是最短的;真要最短,换 BFS;非要 DFS 加剪枝,就得带当前深度参数并维护全局最小值
递归深了、节点多了、路径长了,append 和闭包捕获的切片容易吃内存,这时候得考虑流式处理或分批回调,而不是一股脑攒全量结果。


















