LeetCode 685. Redundant Connection II - 彻底掌握并查集(Union Find)系列题15

给定一个由边组成的有向图,该图起始于一个根节点并增加了额外的一条边。目标是找到可以删除的边,以使图重新成为一棵有向树。此问题可以使用并查集解决,通过处理形成环和多个父节点的情况来确定冗余边。
摘要由CSDN通过智能技术生成

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值