力扣数据中心有 n
台服务器,分别按从 0
到 n-1
的方式进行了编号。它们之间以 服务器到服务器 的形式相互连接组成了一个内部集群,连接是无向的。用 connections
表示集群网络,connections[i] = [a, b]
表示服务器 a
和 b
之间形成连接。任何服务器都可以直接或者间接地通过网络到达任何其他服务器。
关键连接 是在该集群中的重要连接,假如我们将它移除,便会导致某些服务器无法访问其他服务器。
请你以任意顺序返回该集群内的所有 关键连接 。
示例 1:
输入:n = 4, connections = [[0,1],[1,2],[2,0],[1,3]] 输出:[[1,3]] 解释:[[3,1]] 也是正确的。
示例 2:
输入:n = 2, connections = [[0,1]] 输出:[[0,1]]
提示:
2 <= n <= 105
n - 1 <= connections.length <= 105
0 <= ai, bi <= n - 1
ai != bi
- 不存在重复的连接
方法一:Tarjan 算法
此题中的「关键连接」即为「桥」。
「桥」:在一连通的无向图中,若去除某一边后会使得图不再连通,则这条边可以视作「桥」。
与之相应的概念还有「割点」。
「割点」:在一连通的无向图中,若去除某一点及所有与其相连的边后会使得图不再连通,则这个点可以视作「割点」。
用于求图中的「桥」与「割点」有一算法:tarjan 算法,这个算法使用先递归的访问相邻节点后访问节点自身的 dfs 方法,通过记录「访问的顺序:DFN」以及在递归结束后访问节点自身时探索其可以回溯到的最早被访问的节点来更新「最早可回溯的节点:low」,可以实现在
class Solution {
public:
int count = 0;
vector<int> dfn, low;
vector<vector<int>> graph;
vector<vector<int>> res;
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++count;
for (auto& v : graph[u]) {
if (v == fa)
continue;
if (!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (dfn[u] < low[v])
res.push_back({u, v});
} else {
low[u] = min(dfn[v], low[u]);
}
}
}
vector<vector<int>> criticalConnections(int n, vector<vector<int>>& connections) {
dfn.resize(n);
low.resize(n);
graph.resize(n);
for (auto& edge : connections) {
graph[edge[0]].push_back(edge[1]);
graph[edge[1]].push_back(edge[0]);
}
for (int i = 0; i < n; i++) {
if (!dfn[i])
tarjan(i, -1);
}
return res;
}
};