这道题给的是无向图。
一个无向图如果是一个树,那它必须是全连通而且不能有环。
这题有几种做法,比较简单的做法是Union Find, 而且复杂度最好。
另外一种写法是答案里给的DFS,复杂度就是N^ 2了
另外一种写法是先找到环,再找到这个环里最后一个出现的边。这个时间复杂度是 O(N)
先写Union Find的写法。
我的错误是手太熟了,所以写union find的时候写错了,union的时候应该把root1 和root2 hookup,
我手太熟没想写成id1, id2了。
总结一下Union Find易错点。 1。Union的时候容易忘了判断两个root是否相等 2。root和id要分清楚。
class Solution {
public int[] findRedundantConnection(int[][] edges) {
int N = edges.length;
UnionFind uf = new UnionFind(N);
for (int[] edge : edges) {
int a = edge[0] - 1, b = edge[1] - 1;
if (!uf.union(a, b)) return edge;
}
return null;
}
}
class UnionFind {
int[] ids;
int[] weights;
public UnionFind(int N) {
ids = new int[N];
weights = new int[N];
for (int i = 0; i < N; i++) {
ids[i] = i;
weights[i] = 1;
}
}
public int root(int id) {
if (ids[id] != id) {
ids[id] = root(ids[id]);
}
return ids[id];
}
public boolean union(int id1, int id2) {
int root1 = root(id1), root2 = root(id2);
if (root1 == root2) return false;
if (weights[root1] <= weights[root2]) {
ids[root1] = root2; //这里是对root的操作,foget about id2, id1
weights[root2] += weights[root1];
} else {
ids[root2] = root1;
weights[root1] += weights[root2];
}
return true;
}
}
再写第一种DFS的写法。
在进行DFS的时候要注意一点,
由于我们提前进行了预防(不加redundant connection) 所以DFS的时候图一定是没有环的。
因为图是没有环的,所以访问的时候就不需要避免loop了。所以只用一个visited就可以。
class Solution {
public int[] findRedundantConnection(int[][] edges) {
int N = edges.length;
ArrayList[] graph = new ArrayList[N];
Set visited = new HashSet<>();
for (int i = 0; i < N; i++) graph[i] = new ArrayList<>();
for (int[] edge : edges) {
int a = edge[0] - 1, b = edge[1] - 1;
visited.clear();
if (dfs(graph, a, b, visited)) return edge;
graph[a].add(b);
graph[b].add(a);
}
return null;
}
private boolean dfs(ArrayList[] graph, int source, int target, Set visited) {
// no loop here. We find the loop before the loop appeared.
if (visited.contains(source)) return false;
visited.add(source);
if (source == target) return true;
for (int child : graph[source]) {
if (dfs(graph, child, target, visited)) return true;
}
return false;
}
}
再看另一种DFS的解法。
就是先把环找出来,再遍历整个环, 看谁是最后一个出现的点。
这种做法还是ON的时间复杂度。就是用了LinkedHashSet这种数据结构来进行O1查询和保存它出现的顺序。
class Solution {
public int[] findRedundantConnection(int[][] edges) {
int N = edges.length;
ArrayList[] graph = new ArrayList[N];
LinkedHashSet visiting = new LinkedHashSet<>();
for (int i = 0; i < N; i++) graph[i] = new ArrayList<>();
Map edgeId = new HashMap<>();
for (int i = 0; i < edges.length; i++) {
int[] edge = edges[i];
int a = edge[0] - 1, b = edge[1] - 1;
graph[a].add(b);
graph[b].add(a);
edgeId.put(a * N + b, i);
edgeId.put(b * N + a, i);
}
List loop = getLoop(graph, visiting);
int lastId = -1;
for (int i = 0; i < loop.size(); i++) {
int a = loop.get(i), b = (i == loop.size() - 1) ? loop.get(0) : loop.get(i + 1);
if (edgeId.get(a * N + b) > lastId) lastId = edgeId.get(a * N + b);
}
return edges[lastId];
}
private List getLoop(ArrayList[] graph, LinkedHashSet visiting) {
Integer start = dfs(graph, visiting, 0, -1);
List loop = new ArrayList<>();
Iterator it = visiting.iterator();
boolean started = false;
while (it.hasNext()) {
int a = it.next();
if (a == start) {
started = true;
}
if (started) loop.add(a);
}
return loop;
}
private Integer dfs(ArrayList[] graph, LinkedHashSet visiting, int source, int parent) {
if (visiting.contains(source)) return source;
visiting.add(source);
for (int next : graph[source]) {
if (next != parent) {
Integer ans = dfs(graph, visiting, next, source);
if (ans != null) return ans; // find a loop, return the loop starting position
}
}
visiting.remove(source);
return null;
}
}