冗余连接
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/redundant-connection
树可以看成是一个连通且无环的无向图。
给定往一棵 n 个节点(节点值1~n)的树中添加一条边后的图。添加的边的两个顶点包含在1到n中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为n的二维数组edges,edges[i]=[ai,bi]表示图中在ai和bi之间存在一条边。
请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。
示例 1:
输入: edges = [[1,2], [1,3], [2,3]]
输出: [2,3]
示例 2:
输入: edges = [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
提示:
n == edges.length
3 <= n <= 1000
edges[i].length == 2
1 <= ai < bi <= edges.length
ai != bi
edges 中无重复元素
给定的图是连通的
并查集
1.并查集是什么
并查集是一种用来管理元素分组情况的数据结构。并查集可以高效地进行如下操作。不过需要注意并查集虽然可以进行合并操作,但是却无法进行分割操作。
(1)查询元素a和元素b是否属于同一组。
(2)合并元素a和元素b所在的组。
2.并查集的结构
并查集是使用树形结构实现的。
每个元素对应一个节点,每个组对应―棵树。在并查集中,哪个节点是哪个节点的父亲以及树的形状等信息无需多加关注,整体组成一个树形结构才是重要的。
(1)初始化
我们准备n个节点来表示n个元素。最开始时没有边。
(2)合并
像下图一样,从一个组的根向另一个组的根连边,这样两棵树就变成了一棵树,也就把两个组合并为一个组了。
(3)查询
为了查询两个节点是否属于同一组,我们需要沿着树向上走,来查询包含这个元素的树的根是谁。如果两个节点走到了同一个根,那么就可以知道它们属于同一组。
在下图中,元素2和元素5都走到了元素1,因此它们属于同一组。另一方面,由于元素7走到的是元素6,因此同元素2或元素5属于不同组。
3.并查集实现中的注意点
在树形数据结构里,如果发生了退化的情况,那么复杂度就会变得很高。因此,有必要想办法避免退化的发生。在并查集中,只需按照如下方法就可以避免退化。
(1)对于每棵树,记录这棵树的高度(rank)。
(2)合并时如果两棵树的rank不同,那么从rank小的向rank大的连边。
此外,通过路径压缩,可以使得并查集更加高效。对于每个节点,一旦向上走到了一次根节点,就把这个点到父亲的边改为直接连向根。
在此之上,不仅仅是所查询的节点,在查询过程中向上经过的所有的节点,都改为直接连到根上。这样再次查询这些节点时,就可以很快知道根是谁了。
在使用这种简化的方法时,为了简单起见,即使树的高度发生了变化,我们也不修改rank的值。
4.并查集的复杂度
加入了这两个优化之后的并查集效率非常高。对n个元素的并查集进行一次操作的复杂度是O(α(n))。在这里,α(n)是阿克曼 (Ackermann))函数的反函数T。这比O(log(n))还要快。
不过,这是“均摊复杂度”。也就是说,并不是每一次操作都满足这个复杂度,而是多次操作之后平均每一次操作的复杂度是O(α(n))的意思。
var (
n = 1005
parent = make([]int,n) //父亲
rank = make([]int,n) //树的高度
)
//初始化n个元素
func initl(n int){
for i:=0;i<n;i++{
parent[i] = i
rank[i] = 0
}
}
//查询树的根
func find(x int) int{
if parent[x] == x{
return x
}else{
parent[x] = find(parent[x])
return parent[x]
}
}
//合并x和y所属的集合
func unite(x,y int){
x = find(x)
y = find(y)
if x==y{
return
}
if rank[x]<rank[y]{
parent[x] = y
}else{
parent[y] = x
if rank[x] == rank[y]{
rank[x]++
}
}
}
//判断x和y是否属于同一个集合
func same(x,y int) bool{
return find(x) == find(y)
}
func findRedundantConnection(edges [][]int) []int {
initl(n)
for i:=0;i<len(edges);i++{
if same(edges[i][0],edges[i][1]){
return edges[i]
}else{
unite(edges[i][0],edges[i][1])
}
}
return []int{}
}
并查集部分为《挑战程序设计竞赛(第2版)》中的内容,做个笔记,如有错误欢迎大家指正!