Reputation: 21405
I am working on programming where I need to find the articulation points of a graph (nodes such that removing any of them makes the graph disconnected)
For example, I have these links:
Example 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.
Explanation:
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
Example 2:
[[1,2], [2,3], [3,4], [4,5], [6,3]]
The output should be: [2, 3, 4]
Explanation:
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
How to achieve this in a Java program?
Upvotes: 1
Views: 465
Reputation: 224
There are different algorithms used to find the nodes such that if removed they make the graph disconnected (called articulation points).
Here I explain one of them and I provide some code that implements it:
Given a graph we want to find all the
such that if
is removed from
the graph become disconnected
The first observation is that the a (weak) connected component in a directed graph is equal to a connected component in the same graph, but where the edges are undirected. So for simplicity we consider as an undirected graph.
On the graph we run a pre-order Depth First Search (DFS) visit where for any node we assign 2 values, let's call it
pre
and low
. pre
represent the instant when the node is visited and low
the instant of the lowest reachable node from .
The visit works in this way:
At every step of the visit both pre
and low
of are set to the next value of
pre
. Then if we find that a cycle is being closed we set low
to pre
of the start cycle node. low
value is transmitted to parent through DFS backtracking.
When the DFS finish for every couple of nodes such that
and
are neighbor and
low
value of is greater or equal to the
pre
value of then
is an articulation point.
For this there is an exception: the root of the DFS spanning tree is an articulation point only if it has more than 1 children
(In the graph P obviously means pre
and L means low
)
At first pre
and low
of every vertex are set to a default value (let's say -1)
We start from node 0 and set his pre
and low
We go to node 1 and set his pre
and low
We can go to 2 or 3, we decide to go to 2 and set his pre
and low
We can go to 4 or 5, we decide to go to 4 and set his pre
and low
We go to 3 and set his pre
and low
We see that 1 is alredy visited; that means it is a cycle, so we update low
of 3 to pre
of 1
Through backtrack we return to 4 and update his low
value
Through backtrack we return to 2 and update his low
value
Now we go to 5 and set his pre
and low
Through backtrack we return to 2, but there's nothing to do.
We returned from 5 so his low
value is fixed and is greater than pre
value of 2; so 2 is an articulation point
Through backtrack we return to 1, and there's nothing to do.
We returned from 2 so his low
value is fixed and is equal to the pre
value of 1; so 1 is an articulation point
Through backtrack we return to 0, but there's nothing to do.
We returned from 1 so his low
value is fixed and is greater than pre
value of 0; but 0 is the root and has only one child; so it isn't an articulation point
So we have found the answer: [1, 2]
Here is a simple really easy to understand snippet of code (C++) extracted from Competitive Programming Handbook by S. Halim and F. Halim and modified by me.
It is not very adapt to "real word application" (for example because it uses global variables) but it is ok for competitive programming and explaining due to his brevity and clearness.
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);
}
Upvotes: 1
Reputation: 176
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);
}
}
Upvotes: 2