无向图
边没有方向,1-2 等于 2-1。
这题的边 [u,v] 只表示 u 和 v 连上,不表示 u 指向 v。
一棵树本来没有环。现在多加了一条边,图里出现了一个环。我们要找出哪条边是多余的,删掉它之后图又能恢复成树。
给你一个无向图,它原本是一棵树。
后来多加了一条边,所以出现了一个环。
返回那条可以删掉的边。
什么是冗余边?删掉它之后,剩下的图仍然连通,而且不再有环。
边没有方向,1-2 等于 2-1。
这题的边 [u,v] 只表示 u 和 v 连上,不表示 u 指向 v。
连通且无环的图。
任意两点之间原本只有一条简单路径。
两个点之间有路能走到。
不一定直接相邻,中间可以经过别的点。
从一个点出发,绕一圈还能回到自己。
旧路径加上一条新边,就把圈闭合了。
删掉后仍然能保持连通的多余边。
它不是唯一能删的边时,题目要返回输入顺序里让环闭合的那条。
快速判断两个点是否已经在同一组。
适合反复问“之前是否连通”。
每个节点当前指向的集合代表。
parent 数组是并查集的内部账本,不是图上所有边。
一个集合最终的老大。
find(x) 一路沿 parent 找到 root。
这题的核心问题不是求最短路,也不是遍历所有路径,而是反复判断:
如果没连通,就把它们合并。
如果已经连通,再加这条边就会形成环。
这正是并查集擅长的事情。
当前还没有执行 find。
| node | parent[node] |
|---|---|
| 1 | 待初始化 |
| 2 | 待初始化 |
| 3 | 待初始化 |
我们会按顺序处理每一条边。
1func findRedundantConnection(edges [][]int) []int {2 n := len(edges)3 parent := make([]int, n+1)45 for i := 1; i <= n; i++ {6 parent[i] = i7 }89 var find func(int) int10 find = func(x int) int {11 if parent[x] != x {12 parent[x] = find(parent[x])13 }14 return parent[x]15 }1617 union := func(a, b int) bool {18 rootA := find(a)19 rootB := find(b)2021 if rootA == rootB {22 return false23 }2425 parent[rootB] = rootA26 return true27 }2829 for _, edge := range edges {30 a, b := edge[0], edge[1]31 if !union(a, b) {32 return edge33 }34 }3536 return nil37}先给每个点分配一个集合:1 指向 1,2 指向 2,表示大家一开始互不连通。
找到 x 所属集合的最终代表,也就是 root。
parent[x] = find(parent[x]) 会把沿途节点直接挂到 root 下,让后续查询更快。
先比较 rootA 和 rootB。rootA == rootB 说明两个点已经连通,不能再连。
遍历 edges 时第一次 union 失败,当前边就是让环闭合的边。