bfs必须用队列实现,go中用切片模拟:queue=queue[1:]出队、append入队;需先记len(queue)再循环处理当前层;图需visited防重复,树可省;dfs递归简洁但慎用于深图,邻接表是go图表示首选;距离数组用-1或map初始化以区分未访问与不可达。

用 queue 做 BFS,别用递归
BFS 的本质是“一层层推”,必须靠队列(FIFO)保序;递归天然带栈行为,强行套用只会绕晕自己、写成伪BFS。Go 没有内置队列类型,直接用切片模拟最实际——queue = queue[1:] 出队,append(queue, node) 入队即可。
- 常见错误:把
for range queue当作“当前层遍历”,结果边遍历边扩容,导致无限循环或漏层 - 正确做法:每次进入 for 循环前先记下
size := len(queue),只处理这size个节点 - 图场景下必须配
visited map[int]bool,否则无向图会反复横跳(比如 A→B→A→B…) - 树结构可省略
visited,但一旦转成图表示(如邻接表),就必须加,不加必死循环
DFS 用递归还是手写栈?看场景
递归写法简洁,适合树或深度可控的图;但真实图数据可能含长链或环,递归过深会触发 stack overflow,尤其在默认 goroutine 栈仅 2KB 的 Go 中更敏感。
- 推荐默认用递归:代码短、语义清,比如遍历二叉树、做路径回溯(
path切片传指针 + 回退操作) - 必须用手写栈:当图节点数 > 1000 或明确存在超长链(如链状图),用
stack []*Node+pop := stack[len(stack)-1]; stack = stack[:len(stack)-1] - 无论哪种,
visited都不能少;DFS 不像 BFS 那样天然防重入,漏标一个节点就可能重复访问甚至死循环
Graph 表示选邻接矩阵还是邻接表?
邻接矩阵(二维 [][]int)查边快 O(1),但空间爆炸;邻接表(map[int][]int 或 []([]int))省内存、遍历邻居自然,是 Go 中事实标准。
- 稀疏图(边数 ≪ 节点数²)一律用邻接表,比如社交网络、网页链接图
- 邻接矩阵只在节点数固定且极小(≤ 100)、频繁查任意两点是否连通时才值得考虑
- Go 中用
map[int][]int更灵活(支持非连续 ID),但若节点 ID 是 0~n-1,用[]([]int)性能更好、无哈希开销 - 无向图记得双向加边:
g.adj[u] = append(g.adj[u], v)和g.adj[v] = append(g.adj[v], u),漏一边就是半张图
BFS 求最短路径时,距离数组怎么初始化?
不是所有节点都能从起点到达,dist 必须区分“未访问”和“不可达”;用 map[int]int 初始化时,别设默认值 0,否则无法判断节点 0 是“距离为 0”还是“根本没访问到”。
- 安全做法:用
dist := make(map[int]int)空初始化,再显式赋dist[start] = 0;查距离时用if d, ok := dist[node]; ok { ... } - 如果坚持用切片
dist := make([]int, n),初始全填 -1(dist[i] = -1),表示未访问/不可达;更新时只覆盖 -1 值 - 别用
math.MaxInt32类似值初始化——Go int 大小依赖平台,且语义不清;-1 或 map 查存在性更直白
if !visited[node],也别赌它“应该不会重复”。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











