leetcode 685. Redundant Connection II(多余的连接之二)

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]

684题类似,只不过684是无向图,而685是有向图。
有向图的树正常情况下每个节点应该只有一个parent(除了root以外),但是加了一条边,让找出多余的边使有向图变成树。
边[u,v]中,u是v的parent。

思路:
比684题多了一种情况,就是有向图中两个parent的情况。
正常情况下每个节点只有一个parent,而多加的一条边可能使某个节点有了两个parent。分以下两种

case 1:节点有两个parent,但是无闭环,如example1
可通过遍历边,保存每个节点的parent,如果发现某个点v之前已经出现过parent,就说明v有两个parent。如example1中的[1,3], [2,3],遍历到[2,3]时发现3已经有了parent:1。
这时要么需要删除之前出现的那条边[1,3],要么需要删除当前的边[2,3],先把这两条边作为候选分别保存到res1和res2。

case 2:节点有两个parent,其中一条边在闭环中
例如:(图片来自参考资料
在这里插入图片描述
边上的数字代表边遍历的顺序,首先找到了节点1有两个parent,所以保存1号边和2号边,1号边先出现,保存到res1。2号边后出现,保存在res2。

然后找闭环,如果按684题的做法,有闭环时返回的应该是4号边,因为它是造成闭环出现的边(最后出现),但是这里要优先返回有两个parent的边。所以要先看res1中是否有边,有就返回res1中的边。
2号边因为是有两个parent的边中后出现的,满足了题目要求的返回后出现边的条件,如果没有闭环就直接返回res2就行了。所以这一步不需要再check。

剩下的情况就是没有两个parent,只有闭环,这就是684题了,还是union-find,上面的case2找闭环也在此步骤
只是有一trick,因为case1中保存进res2的边在case2中不需要再check,可在check case1时把res2中出现的边换成[-1, -1] ,后面发现边是负的可直接跳过。

而且注意case1中保存的parent,要在后面check闭环前清空。因为闭环又是从头开始check的。

    public int[] findRedundantDirectedConnection(int[][] edges) {
        int m = edges.length;
        int[] parent = new int[m+1];
        int[] size = new int[m+1];
        int[] res1 = new int[2];
        int[] res2 = new int[2];
        
        Arrays.fill(size, 1);
        
        for(int[] edge : edges) {
            int u = edge[0];
            int v = edge[1];
            
            if(parent[v] > 0) {
                //共同parent的边中先出现的
                res1[0] = parent[v]; res1[1] = v;
                //共同parent的边中后出现的
                res2[0] = u;  res2[1] = v;
                //删除后出现的边,后面判断前面出现的边是否有闭环,优先返回有闭环的边
                edge[0] = -1;
                edge[1] = -1;
            }
            parent[v] = u;
        }
        
        Arrays.fill(parent, 0);
        //看剩下的边是否存在闭环,union-find
        for(int[] edge : edges) {
            int u = edge[0];
            int v = edge[1];
            if(u < 0) continue;
            
            if(parent[u] == 0) parent[u] = u;
            if(parent[v] == 0) parent[v] = v;
            
            int pu = findRoot(u, parent);
            int pv = findRoot(v, parent);
            
            if(pu == pv){
                //优先返回有两个parent的边,如果没有,返回造成闭环的第一条边
                return (res1[0] > 0 ? res1 : edge);
            };
            
            if(size[pv] > size[pu]) {
                //swap
                int tmp = pu;
                pu = pv;
                pv = tmp;
            }
            
            parent[pv] = pu;
            size[pu] += size[pv];
        }
        
        //没有闭环,就返回有两个parent的边
        return res2;
    }
    
    int findRoot(int node, int[] parent) {
        while(parent[node] != node) {
            parent[node] = parent[parent[node]];
            node = parent[node];
        }
        return node;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值