LeetCode 685.冗余连接 II 题解
前言
- 题目标签:并查集;树的特性
- 代码耗时:第一次这么快,超越了100%的用户?
- 语言:Python
- 题目链接
题解
-
题目概述
输入一个有向图,该有向图是由n个节点组成的树和一条附加有向边构成。返回一条能删除的边,使得剩下的图是有n个节点的有根树;有根树指满足以下条件的有向图:该树只有一个根节点,所有其他节点都是该根节点的后继。该树除根节点之外的每一个节点都有且只有一个父节点,根节点没有父节点。
-
思路
原有向图本来是一颗树,在此前提下添加了一条边使得有向图不再是一颗树。分析后,向一颗树添加一条不重复边后的结果有两种:
(一)图中某个节点入度为2。
(二)图中出现有向环(即根节点的入度从0变为1)。
由此可以分两步进行:在出现结果一时,找出节点入度为2的点,这个点是两条有向边的head点,找出这两条边。其中一条边删除后,如果有向图变成了树,则输出这条边,否则输出另一条边;在出现结果二时,利用基础查并集方法找到最先无法join的边,输出。对应代码如下:def findRedundantDirectedConnection(self, edges): n = len(edges) in_degree = [0 for _ in range(n+1)] # 记录每个点的入度 two_degree_point = 0 # 记录入度为2的点 two_degree_edge = [] # 记录入度为2对应的两条边 for u, v in edges: in_degree[v] += 1 if in_degree[v] == 2: two_degree_point = v break # 如果出现了结果一 if two_degree_point: for i in range(n): if edges[i][1] == two_degree_point: two_degree_edge.append(edges[i]) two_degree_edge.reverse() if self.is_tree_after_remove_edge(edges, two_degree_edge[0]): return two_degree_edge[0] else: return two_degree_edge[1] # 如果出现了结果二 else: return self.remove_circle_edge(edges)
-
难点及注意事项
- 如上方加粗的文字,怎么判断删除一条边后变成了树呢?第一次不是很容易想到,实际上只需要在edges中删除这条边后,如果其余所有边能够顺利join,则该边就是我们要找的边。对应代码如下
def remove_circle_edge(self, edges): n = len(edges) father = [i for i in range(n+1)] for u, v in edges: if self.is_same(u, v, father): return u, v else: self.join(u, v, father)
- 注意题目要求“若有多个答案,返回最后出现在给定二维数组的答案。”,需要把得到的two_degree_edge反转一下。
整体代码如下
class Solution:
def findRedundantDirectedConnection(self, edges):
n = len(edges)
in_degree = [0 for _ in range(n+1)] # 记录每个点的入度
two_degree_point = 0 # 记录入度为2的点
two_degree_edge = [] # 记录入度为2对应的两条边
for u, v in edges:
in_degree[v] += 1
if in_degree[v] == 2:
two_degree_point = v
break
# 如果出现了结果一
if two_degree_point:
for i in range(n):
if edges[i][1] == two_degree_point:
two_degree_edge.append(edges[i])
two_degree_edge.reverse()
if self.is_tree_after_remove_edge(edges, two_degree_edge[0]):
return two_degree_edge[0]
else:
return two_degree_edge[1]
# 如果出现了结果二
else:
return self.remove_circle_edge(edges)
def find(self, u, father):
if u != father[u]:
father[u] = self.find(father[u], father)
return father[u]
def is_same(self, u, v, father):
return self.find(u, father) == self.find(v, father)
def join(self, u, v, father):
u = self.find(u, father)
v = self.find(v, father)
if u != v:
father[v] = u
def is_tree_after_remove_edge(self, edges, target):
n = len(edges)
father = [i for i in range(n+1)]
for edge in edges:
if edge == target:
continue
u = edge[0]
v = edge[1]
if self.is_same(u, v, father): # 这表明u v已经连通,如果再加上(u, v)则冗余,故return False
return False
else:
self.join(u, v, father)
return True
def remove_circle_edge(self, edges):
n = len(edges)
father = [i for i in range(n+1)]
for u, v in edges:
if self.is_same(u, v, father):
return u, v
else:
self.join(u, v, father)
s = Solution()
print(s.findRedundantDirectedConnection([[1,2],[1,3],[2,3]]))