我正在进行编程,需要找到图形的铰接点(删除所有节点会使图形断开连接的节点)
例如,我有以下链接:
示例1
[[0,1], [0,2], [1,3], [2,3], [5,6], [3,4]]
The answer should be [2,3,5], because removing these nodes makes the graph disconnected.
说明:
If I remove node 2 here, the graph becomes 2 parts 0,1,3,4 and 5,6
If I remove node 3 here, the graph becomes 2 parts 0,1,2,5,6 and 4
If I remove node 5 here, the graph becomes 2 parts 0,1,2,3,4 and 6
示例2:
[[1,2], [2,3], [3,4], [4,5], [6,3]]
The output should be: [2, 3, 4]
说明:
If I remove node 2 here, the graph becomes 2 parts 1, and 3,4,5,6
If I remove node 3 here, the graph becomes 3 parts 1,2 and 6 and 4,5
If I remove node 4 here, the graph becomes 2 parts 1,2,3,6 and 5
如何在Java程序中实现这一目标?
答案 0 :(得分:2)
import static java.lang.Math.min;
import java.util.ArrayList;
import java.util.List;
public class ArticulationPointsAdjacencyList {
private int n, id, rootNodeOutcomingEdgeCount;
private boolean solved;
private int[] low, ids;
private boolean[] visited, isArticulationPoint;
private List<List<Integer>> graph;
public ArticulationPointsAdjacencyList(List<List<Integer>> graph, int n) {
if (graph == null || n <= 0 || graph.size() != n) throw new IllegalArgumentException();
this.graph = graph;
this.n = n;
}
// Returns the indexes for all articulation points in the graph even if the
// graph is not fully connected.
public boolean[] findArticulationPoints() {
if (solved) return isArticulationPoint;
id = 0;
low = new int[n]; // Low link values
ids = new int[n]; // Nodes ids
visited = new boolean[n];
isArticulationPoint = new boolean[n];
for (int i = 0; i < n; i++) {
if (!visited[i]) {
rootNodeOutcomingEdgeCount = 0;
dfs(i, i, -1);
isArticulationPoint[i] = (rootNodeOutcomingEdgeCount > 1);
}
}
solved = true;
return isArticulationPoint;
}
private void dfs(int root, int at, int parent) {
if (parent == root) rootNodeOutcomingEdgeCount++;
visited[at] = true;
low[at] = ids[at] = id++;
List<Integer> edges = graph.get(at);
for (Integer to : edges) {
if (to == parent) continue;
if (!visited[to]) {
dfs(root, to, at);
low[at] = min(low[at], low[to]);
if (ids[at] <= low[to]) {
isArticulationPoint[at] = true;
}
} else {
low[at] = min(low[at], ids[to]);
}
}
}
/* Graph helpers */
// Initialize a graph with 'n' nodes.
public static List<List<Integer>> createGraph(int n) {
List<List<Integer>> graph = new ArrayList<>(n);
for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
return graph;
}
// Add an undirected edge to a graph.
public static void addEdge(List<List<Integer>> graph, int from, int to) {
graph.get(from).add(to);
graph.get(to).add(from);
}
/* Example usage: */
public static void main(String[] args) {
testExample2();
}
private static void testExample1() {
int n = 7;
List < List < Integer >> graph = createGraph (n);
addEdge (graph, 0, 1);
addEdge (graph, 0, 2);
addEdge (graph, 1, 3);
addEdge (graph, 2, 3);
addEdge (graph, 2, 5);
addEdge (graph, 5, 6);
addEdge (graph, 3, 4);
ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
boolean[] isArticulationPoint = solver.findArticulationPoints();
// Prints:
// Node 2 is an articulation
// Node 3 is an articulation
// Node 5 is an articulation
for (int i = 0; i < n; i++)
if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
}
private static void testExample2() {
int n = 7;
List < List < Integer >> graph = createGraph (n);
addEdge (graph, 1, 2);
addEdge (graph, 2, 3);
addEdge (graph, 3, 4);
addEdge (graph, 3, 6);
addEdge (graph, 4, 5);
ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
boolean[] isArticulationPoint = solver.findArticulationPoints();
// Prints:
// Node 2 is an articulation
// Node 3 is an articulation
// Node 4 is an articulation
for (int i = 0; i < n; i++)
if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
}
}
答案 1 :(得分:1)
有多种算法可用于查找节点,因此,如果删除这些节点,则会使图形断开连接(称为铰接点)。
在这里我解释其中之一,并提供一些实现它的代码:
给定一个图,我们希望找到所有的
,这样,如果从
中删除了
,则该图将断开连接。
第一个观察结果是,有向图中的a(弱)连通分量等于同一图中的连通分量,但是边是无向的。因此,为简单起见,我们将视为无向图。
在该图上,我们进行了预定的深度优先搜索(DFS)访问,其中为任何节点分配2个值,我们将其称为
pre
和low
。 pre
代表访问该节点的时刻,low
代表来自的最低可达节点的时刻。
这种访问方式是这样的:
在访问的每个步骤中,的
pre
和low
都被设置为pre
的下一个值。然后,如果发现某个循环正在关闭,则将起始循环节点的low
设置为pre
。 low
值通过DFS回溯传输给父级。
每隔两个节点的DFS完成时,使得
和
是邻居,并且
的
low
值大于或等于的
pre
值,则是一个连接点。
为此有一个例外:DFS生成树的根只有在有多个子代的情况下才是关节点
(在图形中,P显然表示pre
,L表示low
)
首先,将每个顶点的pre
和low
设置为默认值(假设为-1)
我们从节点0开始,设置他的pre
和low
我们转到节点1并设置其pre
和low
我们可以转到2或3,我们决定转到2并设置他的pre
和low
我们可以转到4或5,我们决定转到4并设置他的pre
和low
我们转到3并设置他的pre
和low
我们看到1已被访问;这意味着这是一个周期,因此我们将3的low
更新为1的pre
通过回溯,我们返回4并更新他的low
值
通过回溯,我们返回2并更新其low
值
现在我们转到5并设置他的pre
和low
通过回溯,我们返回到2,但是没有任何事情要做。
我们从5返回,因此他的low
值是固定的,并且大于pre
的值2;所以2是一个关节点
通过回溯,我们返回到1,无事可做。
我们从2返回,因此他的low
值是固定的,等于pre
的值1;所以1是一个关节点
通过回溯,我们返回到0,但是没有任何事情要做。
我们从1返回,因此他的low
值是固定的,并且大于pre
的值0;但是0是根,并且只有一个孩子;所以它不是一个关节点
因此,我们找到了答案:[1, 2]
这是S. Halim和F. Halim从《竞争编程手册》中提取并由我修改的一个非常简单易懂的代码片段(C ++)。
它不太适合“实词应用”(例如,因为它使用全局变量),但由于其简洁明了,因此可以进行竞争性编程和解释。
const int UNVISITED = -1;
vector<int> dfs_low;
vector<int> dfs_pre;
int dfsNumberCounter;
int rootChildren;
vector<vector<int>> AdjList;
vector<int> articulation_vertex;
// This function is the DFS that implement Tarjan algoritm
void articulationPoint(int u) {
dfs_low[u] = dfs_pre[u] = dfsNumberCounter++; // dfs_low[u] <= dfs_pre[u]
for (int j = 0; j < (int)AdjList[u].size(); j++) {
int v = AdjList[u][j];
if (dfs_pre[v] == UNVISITED) { // a tree edge
dfs_parent[v] = u;
if (u == dfsRoot) rootChildren++; // special case if u is a root
articulationPoint(v);
if (dfs_low[v] >= dfs_pre[u]) // for articulation point
articulation_vertex[u] = true; // store this information first
dfs_low[u] = min(dfs_low[u], dfs_low[v]); // update dfs_low[u]
}
else if (v != dfs_parent[u]) // a back edge and not direct cycle
dfs_low[u] = min(dfs_low[u], dfs_pre[v]); // update dfs_low[u]
} }
// Some driver code
int main() {
... //Init of variables and store of the graph inside AdjList is omitted
... // V is the number of nodes
dfsNumberCounter = 0;
dfs_pre.assign(V, UNVISITED);
dfs_low.assign(V, 0);
dfs_parent.assign(V, 0);
articulation_vertex.assign(V, 0);
rootChildren = 0;
articulationPoint(0);
if (root_children > 1) {
articulation_vertex[0] = false;
}
printf("Articulation Points:\n");
for (int i = 0; i < V; i++)
if (articulation_vertex[i])
printf(" Vertex %d\n", i);
}