连通图(并查集)

题目链接:https://leetcode.com/problems/redundant-connection-ii/description/

题目截图:

 

题目大意:给定一个二维数组代表一个有向图(数组中每个一维数组[A,B]代表由A指向B的边),图中共有N个结点(标号为1-N)和N条有向边,我们需要删除一条边,使这个有向图变成一个具有根节点的连通树,找出这条边并返回(如果有多种可能,返回靠后的边)。

解题报告:

题目中给出的有向图有三种情况:

1. 某一个结点有两个父节点,图中无环。

2. 除根结点外每个结点都只有一个父节点,图中有环。

3. 某一个结点有两个父节点,图中有环。

对于以上三种情况,有如下两种考虑:

1. 如果存在某一个结点有两个父节点,记录下指向这个结点的两条边,那么需要删除的边必然在这两条边中选择。我们首先将靠后的那条边除去,看看剩下的有向图中是否存在环,如果不存在环,则说明我们的选择是对的,直接返回靠后的边,如果仍然存在环,说明之前的选择是错误的,需要回靠前的边。

2. 如果不存在某一个结点有两个父节点,那么说明有向图中必然出现了环。我们只需要找到哪条边使得有向图出现了环,并且返回。

题目理解到这里,思路就很清晰,难点在于如何判断有向图是否出现了环?这里引入并查集的概念,如果不了解,可以查看另外一篇博客http://blog.csdn.net/u013546077/article/details/64509038,然后再回来看代码。

代码:

 

class Solution {
    public int[] findRedundantDirectedConnection(int[][] edges) {
        int[] can1={-1,-1};
        int[] can2={-1,-1};
        int len=edges.length;
        int[] root=new int[len+1];
        for(int i=0;i<len;i++){
            int[] edge=edges[i];
            if(root[edge[1]]==0){
                root[edge[1]]=edge[0];
            }else{
                can2=new int[]{edge[0],edge[1]};
                can1=new int[]{root[edge[1]],edge[1]};
                edge[1]=0;
            }
        }
        for(int i=0;i<=len;i++){
            root[i]=i;
        }
        for(int[] edge:edges){
            if(edge[1]==0){
                continue;
            }
            if(find(root,edge[0])==find(root,edge[1])){
                if(can1[0]==-1){
                    return edge;
                }else{
                    return can1;
                }
            }else{
                root[edge[1]]=edge[0];
            }
        }
        return can2;
    }
    public int find(int[] root,int p){
        int i=p;
        while(i!=root[i]){
            i=root[i];
        }
        int j=p;
        while(root[j]!=i){
            int temp=root[j];
            root[j]=i;
            j=temp;
        }
        return i;
    }
}

 

代码解释:

1.使用can1和can2来存某个拥有两个父节点的结点的两条边,如果存在这样的结点,先将can2这条边断开,如果不存在则不做处理。

2.循环加入每条边,判断当前有向图是否形成了环,如果整个遍历过程都没有出现环的话,说明第一步中断开的can2这条边就是我们需要删除的;如果遍历过程中出现了环,并且存在can1和can2两条边,则说明我们第一步中断开的can2不是需要删除的,那么我们直接返回can1就好;如果历过程中出现了环并且不存在can1和can2,那么直接返回导致环的这条边就好。

 

posted @ 2018-03-04 21:40  xinyilovestudy  阅读(580)  评论(0编辑  收藏  举报