题目地址:
代码如下
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Solution {
public List<List<Integer>> criticalConnections(int n, List<List<Integer>> connections) {
List<List<Integer>> res = new ArrayList<>();
List<List<Integer>> graph = buildGraph(connections, n);
int[] ord = new int[n], low = new int[n];
boolean[] visited = new boolean[n];
for (int i = 0; i < n; i++) {
// 找到一个还没访问过的连通分量
if (!visited[i]) {
// 将时间戳0传进去
dfs(i, i, 0, ord, low, graph, visited, res);
}
}
return res;
}
// cur是当前遍历的点,parent是cur上一层递归遍历的点,cnt表示访问cur的时间戳(从0开始计数),
// ord记录每个顶点被访问的时间戳,low记录每个顶点能到达的时间戳最小的点的时间戳(这里的到达指的是非回头路的那种到达)
private void dfs(int cur, int parent, int cnt, int[] ord, int[] low, List<List<Integer>> graph, boolean[] visited, List<List<Integer>> res) {
// 标记当前顶点为已访问
visited[cur] = true;
// 先初始化cur的ord和low为当前访问的时间戳
ord[cur] = cnt;
low[cur] = cnt;
// 遍历cur的邻接点
for (int next : graph.get(cur)) {
// 对cur的邻接点进行算法逻辑。
// 如果未访问,则对其进行访问,回溯的时候看一下cur与next的边是否是桥,并更新low[cur];
// 如果不是parent但是已经访问,则直接更新low[cur];
// 如果是parent那直接略过
if (!visited[next]) {
// 将时间戳加上1传递到下一层递归里去
dfs(next, cur, cnt + 1, ord, low, graph, visited, res);
// 发现了桥,加进答案中
if (low[next] > ord[cur]) {
res.add(Arrays.asList(cur, next));
}
// 回溯之前要用low[next]来更新low[cur]
low[cur] = Math.min(low[cur], low[next]);
} else if (next != parent) {
// 如果next已经被访问过,但不是parent,那么cur就有可能从另一条路到达时间戳更早的点,更新low[cur]
low[cur] = Math.min(low[cur], low[next]);
}
}
}
// 邻接表建图
private List<List<Integer>> buildGraph(List<List<Integer>> connections, int n) {
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
for (List<Integer> connection : connections) {
int u = connection.get(0), v = connection.get(1);
graph.get(u).add(v);
graph.get(v).add(u);
}
return graph;
}
}