题目截图:
题目大意:给定一个二维数组代表一个有向图(数组中每个一维数组[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
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,那么直接返回导致环的这条边就好。