心路历程:
这道题属于图论的经典连通问题,这道题翻译过来就是,找到破开图中环的一条边;再翻译过来就是,从后往前遍历edges,依次连接边,当发现新连接的边已经有相同父节点时(已经马上成环了),那么就把这个新的边返回,这就是一个多余的边。
方法就是并查集。
并查集的核心其实在于father数组初始化成range(n)以及单向递归的find函数。核心理论就是无论什么时候总会有一个father中的元素满足father[x] = x,从而结束递归的查找。
注意的点:
1、rank可以用可以不用,只是压缩路径用的,用的话注意初始化都为1
2、注意需要从后往前遍历
解法一:并查集+不压缩路径
class Solution:def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:# 就是把环给破开就行# 并查集:如何根据边的关系建立图?->建立指向同一个father的集合即可n = len(edges)uf = UF(n)for x, y in edges:if uf.union(x, y) == 0: return [x,y]class UF:def __init__(self, n):self.father = [i for i in range(n+1)] # 因为图中结点是1开头的,所以第0位置让出去了def find(self, x):if x == self.father[x]: return x # 找到了父节点,父节点是一直没有变过的初始化就有的结点return self.find(self.father[x])def union(self, x, y):fx, fy = self.find(x), self.find(y)if fx == fy: return 0 # 证明不需要连接self.father[fy] = fxreturn 1
解法二:并查集+压缩路径
class Solution:def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:n = len(edges)uf = UF(n)for x, y in edges:if uf.union(x, y) == 0: return [x,y]class UF:def __init__(self, n):self.father = [i for i in range(n+1)] # 因为图中结点是1开头的,所以第0位置让出去了self.rank = [1]*(n+1) # 表示从父结点到最远子结点的距离def find(self, x):if x == self.father[x]: return x # 找到了父节点,父节点是一直没有变过的初始化就有的结点return self.find(self.father[x])def union(self, x, y):fx, fy = self.find(x), self.find(y)if fx == fy: return 0 # 证明不需要连接# 下面这段是用于压缩路径的;树的秩大的当爹,一样大的时候就指定一个,然后将其秩+1rfx, rfy = self.rank[fx], self.rank[fy]if rfx > rfy: self.father[fy] = fxelif rfx < rfy: self.father[fx] = fyelse:self.father[fy] = fxself.rank[fx] += 1return 1