In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) for which all other nodes are descendants of this node, plus every node has exactly one parent, except for the root node which has no parents.
The given input is a directed graph that started as a rooted tree with n
nodes (with distinct values from 1
to n
), with one additional directed edge added. The added edge has two different vertices chosen from 1
to n
, and was not an edge that already existed.
The resulting graph is given as a 2D-array of edges
. Each element of edges
is a pair [ui, vi]
that represents a directed edge connecting nodes ui
and vi
, where ui
is a parent of child vi
.
Return an edge that can be removed so that the resulting graph is a rooted tree of n
nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array.
Example 1:
Input: edges = [[1,2],[1,3],[2,3]] Output: [2,3]
Example 2:
Input: edges = [[1,2],[2,3],[3,4],[4,1],[1,5]] Output: [4,1]
Constraints:
n == edges.length
3 <= n <= 1000
edges[i].length == 2
1 <= ui, vi <= n
ui != vi
这题是上一题LeetCode 684. Redundant Connection
的拓展,无向图变成了有向图,难度瞬间提高。好在题目的条件是只增加一条边使得原本的图不能形成一棵树。题目中说一个有向图要是一棵树那必须是满足以下条件:只有一个根节点(无父节点),其余节点有且只有一个直系父节点,图中不能存在环。
增加一条边破坏一棵树有三种可能:形成了一个环;使得一个节点有2个直系父节点(即有2条指向该节点的边);即形成一个环又使得一个节点有2个父节点。
当然,既然是上一题LeetCode 684. Redundant Connection
的拓展,那自然还是可以用并查集(Union Find)来解答,只是在合并时处理稍微麻烦点。在合并两个节点时需要判断是否遇到一个环或者是否造成一个节点有2个直系父节点,有下两种情况:
1)当遇到一条边造成当前节点有2个父节点时,如果在这之前环已经存在,那么当前节点的前一条边就是所求的多余边;如果之前环还不存在,那无法确定哪条边是答案,只好先记下这条边并且标识已遇到有两个父节点的情况,如果直到最后也没出现环,那记下的这条边就为答案。
2)当遇到一条边造成环的形成时,如果在之前已经遇到某个节点有两个父节点的情况,那么那个节点的在环里那条边为答案;如果之前没出现过2个父节点的情况,那无法确定,先记下当前边并且表示已遇到环的情况,如果直到最后也没出现2个父节点的情况,那记下的这条边就为答案。
注:这题需要对Union Find的find()和merge()稍作修改。 find()函数中不对节点的parent做更新使得节点的parent永远是直系父节点;把merge()中的查找根父节点拿到函数外。
class UnionFind:
def __init__(self, n) :
self.parent = list(range(0, n))
#self.size = [1] * n
def find(self, x):
if self.parent[x] != x :
return self.find(self.parent[x])
return x
def merge(self, px, py) :
#px, py = self.find(x), self.find(y)
if px == py:
return False
self.parent[py] = px
return True
class Solution:
def findRedundantDirectedConnection(self, edges: List[List[int]]) -> List[int]:
n = len(edges)
uf = UnionFind(n + 1)
cycle, twoParent = False, False
res = []
for [x, y] in edges:
px, py = uf.find(x), uf.find(y)
if y != py: #已经有父节点,表示有两条边指向这个顶点,其中一条可删除
if cycle : #在这之前环已存在,则第一条为可删除边
return [uf.parent[y], y]
#目前为止环不存在,不确定删哪一条边,先记录下来
twoParent = True
res = [x, y]
elif px == py: #产生环
if twoParent: #在这之前出了多个父节点的顶点, 返回那个顶点在环里的那条边
return [uf.parent[res[1]], res[1]]
#目前为止还没出现有顶点有两个父节点, 记录下当前边
cycle = True
res = [x, y]
else:
uf.merge(x, y)
return res