685. 冗余连接 II
题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/redundant-connection-ii
题目
在本问题中,有根树指满足以下条件的有向图。该树只有一个根节点,所有其他节点都是该根节点的后继。每一个节点只有一个父节点,除了根节点没有父节点。
输入一个有向图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。 每一个边 的元素是一对 [u, v],用以表示有向图中连接顶点 u 和顶点 v 的边,其中 u 是 v 的一个父节点。
返回一条能删除的边,使得剩下的图是有N个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。
示例 1:
输入: [[1,2], [1,3], [2,**3**]]
输出: [2,3]
解释: 给定的有向图如下:
1
/ \
v v
2-->3
示例 2:
输入: [[1,2], [2,3], [3,4], [4,1], [1,5]]
输出: [4,1]
解释: 给定的有向图如下:
5 <- 1 -> 2
^ |
| v
4 <- 3
注意:
- 二维数组大小的在3到1000范围内。
- 二维数组中的每个整数在1到N之间,其中 N 是二维数组的大小。
解题思路
思路:并查集
关于并查集,这里推荐一个视频,可以入门了解并查集的内容。(来源:b 站 正月点灯笼)
本题中,涉及的是有向图。在这里,建议可以先尝试下面的题目。第 684 题,涉及的是无向图,可以使用并查集判断是否有环,相信尝试之后会有所启发。
在这里,题目要求的是将给定的有向图,经删除一条边之后,使得剩下的图是有 N 个节点的有根树。
在这里,题目给出有根树的概念:
有根树指满足以下条件的有向图。该树只有一个根节点,所有其他节点都是该根节点的后继。每一个节点只有一个父节点,除了根节点没有父节点
这里我们结合题目的示例来看下,如何能构成有根树?
删除边之前
删除边之后(可能情况)
先看删除边之后(可能情况),结合前面有根树的概念,看看有根树的特点,(这里涉及到拓扑排序的入度概念):
- 有根树的根节点没有父节点,那么在有向图中,它的入度就是 0。如图 2 中,示例 1 和示例 2 的节点 1。
- 除根节点,其他节点都只有一个父节点,也就是入度都为 1。(如图 2 所示除根之外的其他节点)
再对比删除边前后,看其中出现的变化:
- 示例 1 中,节点 3 的入度由 2 变为 1。那么也就说,有根树不能存在入度为 2 的情况;
- 示例 2 中,虽然不存在入度为 2 的情况,但是有向图存在环,所以也不是有根树。
根据前面的分析,整下思路:
- 首先考虑节点入度为 2 的情况,当存在这种情况时,我们可以考虑先删除其中一条边。但是需要注意另外一种情况,若删除边之后又出现有向图存在环的情况,那么就要重新考虑。
- 若不存在入度为 2 的情况,那么这里就主要考虑存在环的情况:
- 这里尝试删除入度 1 的边,判断是否还存在环,不存在则删除;否则继续尝试。
实现的代码如下。
class UnionFind:
"""并查集
"""
def __init__(self, n):
self.parent = [i for i in range(n)]
def find(self, x):
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
x_root = self.find(x)
y_root = self.find(y)
if x_root == y_root:
return False
self.parent[x_root] = y_root
return True
class Solution:
def findRedundantDirectedConnection(self, edges: List[List[int]]) -> List[int]:
def is_circle(edges, n, idx):
# 判断去除边是否存在环
uf = UnionFind(n+1)
for i in range(n):
# 去除边
if i == idx:
continue
if not uf.union(edges[i][0], edges[i][1]):
# 存在环
return True
return False
n = len(edges)
# 记录入度情况
indegree = [0 for _ in range(n+1)]
for edge in edges:
indegree[edge[1]] += 1
# 考虑入度 2 的情况
# 题目要求重复返回最后出现的答案,
# 这里直接逆序遍历
for i in range(n-1, -1, -1):
if indegree[edges[i][1]] == 2:
# 判断去除边是否存在环
if not is_circle(edges, n, i):
return edges[i]
# 入度 1,存在环,尝试去除某一边
for i in range(n-1, -1, -1):
if indegree[edges[i][1]] == 1:
# 判断去除边是否存在环
if not is_circle(edges, n, i):
return edges[i]
欢迎关注
公众号 【书所集录】