LeetCode 685. 冗余连接 II | Python

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 站 正月点灯笼

【算法】并查集(Disjoint Set)[共3讲]

本题中,涉及的是有向图。在这里,建议可以先尝试下面的题目。第 684 题,涉及的是无向图,可以使用并查集判断是否有环,相信尝试之后会有所启发。

684. 冗余连接

在这里,题目要求的是将给定的有向图,经删除一条边之后,使得剩下的图是有 N 个节点的有根树。

在这里,题目给出有根树的概念:

有根树指满足以下条件的有向图。该树只有一个根节点,所有其他节点都是该根节点的后继。每一个节点只有一个父节点,除了根节点没有父节点

这里我们结合题目的示例来看下,如何能构成有根树?

删除边之前

图 1

删除边之后(可能情况

图 2

先看删除边之后(可能情况),结合前面有根树的概念,看看有根树的特点,(这里涉及到拓扑排序的入度概念):

  • 有根树的根节点没有父节点,那么在有向图中,它的入度就是 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]

欢迎关注


公众号 【书所集录

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值