邻接表更实用,尤其适用于真实业务中的稀疏图(如网页链接、交通网络);邻接矩阵适合顶点数少且边数接近v²的稠密图,查边O(1)但内存固定占O(v²)。

邻接表 vs 邻接矩阵:什么时候该用 map[int][]int
邻接矩阵适合顶点数 v*v 的稠密图(比如全连接模拟),查边是 O(1),但内存固定占 O(v*v),加点删点要整体复制数组;而邻接表(如 map[int][]int 或 map[string][]string)更适合网页链接、依赖图、社交关系这类稀疏图——边数远小于顶点平方。
常见错误现象:[20][20]float64 写死大小的邻接矩阵,在节点动态增减时会 panic 或静默越界;生产环境几乎不用二维数组硬编码。
- 用
map[int][]int表示无权有向图:添加边只需g.vertices[v1] = append(g.vertices[v1], v2) - 无向图需双向添加:
g.vertices[v1] = append(g.vertices[v1], v2); g.vertices[v2] = append(g.vertices[v2], v1) - 若需权重,改用
map[int][]Edge,其中type Edge struct { To, Weight int }
DFS递归写法在Go中容易栈溢出,必须改用显式栈
Go 默认 goroutine 栈只有 2KB,深度优先搜索遇到长链(比如 5000 层树)直接触发 runtime: goroutine stack exceeds 1000000000-byte limit 崩溃——这不是逻辑错误,是机制限制。
递归 DFS 在 Go 中仅适用于已知深度可控的小图(depth );生产环境一律用显式栈:
立即学习“go语言免费学习笔记(深入)”;
- 初始化:
stack := []int{start},配合visited map[int]bool防重入 - 安全弹出:
v := stack[len(stack)-1]; stack = stack[:len(stack)-1] - 避免脏数据:
stack = append(stack[:len(stack)-1], ...)可能因底层数组复用导致残留旧值
BFS必须用 queue,且空图不处理会 panic index out of range
BFS靠队列保序,若误用切片尾部追加 + 头部切片(q = q[1:])看似能跑,但大量小切片会触发频繁内存分配;更糟的是,空图输入时忘记检查 len(vertices) == 0,queue[0] 直接 panic。
- 初始化前先判断:
if len(g.vertices) == 0 { return []int{} } - 用
queue := []int{start}模拟队列,pop 用v := queue[0]; queue = queue[1:] - 注意:虽然
queue[1:]效率不如双端队列,但对中小规模图足够;真要极致性能可换container/list,但多数场景没必要
有向图、无向图、加权图的边添加逻辑差异直接影响遍历结果
同一个 AddEdge 方法,实现稍有偏差,BFS/DFS 输出顺序就可能完全不同,甚至漏节点。
- 无向图:必须双向添加边,否则
BFS(start)只能访问到出边方向的子图 - 有向图:只单向添加,
AddEdge(u, v)表示u → v,不能反推 - 加权图:若用
map[int][]Edge,遍历时需解构edge.To和edge.Weight,别直接拿neighbor当整数用 - 字符串节点名:用
map[string][]string更直观,但要注意 key 是否存在再取值,否则range g.vertices["X"]会 panic
g.vertices = make(map[int][]int),或者在添加边前没检查节点是否存在。这些不会报编译错,但运行时一访问就 panic。与其花时间 debug 遍历逻辑,不如先确保 AddEdge 调用后,g.vertices[v] 确实包含预期邻居。


















