LeetCode685 Redundant Connection2
this time, we will be given a directed graph, and we need to find that deplicate connected vertices
the leetcode solution provides a way in dfs.
and most of the solution provide by other users are used in Union Find.
so let’s first take a look at the dfs:
the dfs solution is really nasty for this problem. and i don’t even understand that.
for most people, the union find solution is much better:
first. let’s think about how to make a graph into a valid tree.
there are two cases will make a graph not a valid tree as well, the first is: a node having two parents. and the second is a circle exists.
but let’s think about this: why we haven;t thought of checking every node to make sure it doesn;t have two parents for LC684? the answer is simple: the graph is undirected. which means if a node has more than 2 nodes connected to it, we can see it as all those as its children. instead of parents. but with directed graph, if we have more than two nodes points to the same node, then it is valid because they are definitly have more than one parent.
so our working can be divide into two parts:
checking whether there is a node a having two parents.
and use union find to detect the cycle.
class Solution {
public int[] findRedundantDirectedConnection(int[][] edges) {
// can1 is before can2 and has higher priority
int[] can1 = {-1, -1};
int[] can2 = {-1, -1};
int[] roots = new int[edges.length + 1];
for (int[] edge : edges) {
int father = edge[0];
int child = edge[1];
if (roots[child] == 0) {
roots[child] = father;
} else if (roots[child] != 0) {
// the child already has father
// the newest link
can2 = new int[]{father, child};
// the older version of link
can1 = new int[]{roots[child], child};
// set the current link invalid, this way, we makes every node have only one father
edge[1] = 0; //we modify the input edge before we use it later
}
}
// reuse the roots matrix, do union find
for (int i = 0; i < roots.length; i++) {
roots[i] = i;
}
for (int[] edge : edges) {
int father = edge[0];
int child = edge[1];
if (child == 0) {
// current link is not valid
continue;
}
if (find(roots, father) == child) { //if there is a cycle, then we must return it here(because the later if else)
// there is a cycle
//if we have old father-child pair, then we retrun that, else, we return current edge
if (can1[0] == -1) {
// candidate not exist
return edge;
} else {
// candidate exist
return can1;
}
}
// union
roots[child] = father;
}
//if after we tranverse every edge and not find any cycle, then we have to cut the father-child edge which the child has more than one parent. now he is cutted, so this child will only have one.
return can2;
}
private int find(int[] roots, int id) {
while (id != roots[id]) {
// path compression, optional
roots[id] = roots[roots[id]]; //as you can see, the root[] is changing during the find period, so id wil be directed connected to its root, so that's why it called path compression.
id = roots[id];
}
return id;
}
}
so the general idea is: we iterate input edge twice, the first iterate will be check if the current child node have more than one parent. we preserve the old father-child pair and the new father child pair. and we wil make current edge invalids.
and for the second round to iterate edges, we check if there is a cycle by constrcut the union set and check the same time. but we need to check first by recall the find function. why? because if we find that cyrrent child is the root of its father, then there definatly a cycle, and if we don’t have multiple parent for a child, then just return current edge, but if we have such child, then we return that older version link. and after all these, we do the union.
if after we tranverse every edge and not find any cycle, then we have to cut the father-child edge which the child has more than one parent. now he is cutted, so this child will only have one.

浙公网安备 33010602011771号