LeetCode 685.冗余连接 II 题解

LeetCode 685.冗余连接 II 题解

前言

  • 题目标签:并查集;树的特性
  • 代码耗时:第一次这么快,超越了100%的用户?
  • 语言:Python
  • 题目链接

题解

  1. 题目概述
    输入一个有向图,该有向图是由n个节点组成的树和一条附加有向边构成。返回一条能删除的边,使得剩下的图是有n个节点的有根树;有根树指满足以下条件的有向图:该树只有一个根节点,所有其他节点都是该根节点的后继。该树除根节点之外的每一个节点都有且只有一个父节点,根节点没有父节点。
    例子

  2. 思路
    原有向图本来是一颗树,在此前提下添加了一条边使得有向图不再是一颗树。分析后,向一颗树添加一条不重复边后的结果有两种:
    (一)图中某个节点入度为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)
    
  3. 难点及注意事项

    • 如上方加粗的文字,怎么判断删除一条边后变成了树呢?第一次不是很容易想到,实际上只需要在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]]))

参考资料

代码随想录-图论-17.冗余连接II

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值