1

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]]

enter image description here

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]]

enter image description here

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?

Marco Zamboni
  • 204
  • 1
  • 9
learner
  • 4,858
  • 9
  • 47
  • 92
  • I think this is related with "min cut" problem https://www.geeksforgeeks.org/minimum-cut-in-a-directed-graph/ – rvazquezglez Nov 17 '19 at 22:26
  • 1
    There are several well-known linear-time algorithms to do this; some can be found in other answers to [similar](https://stackoverflow.com/questions/28917290/how-can-i-find-bridges-in-an-undirected-graph) [questions](https://stackoverflow.com/a/11221469/15472) – tucuxi Nov 19 '19 at 22:23
  • I've removed the language-specific tag from the question. Since the lack of an attempt included in the question. Even after which, it's worth asking a question after an attempt that you've made and sharing where did it fail or raised a doubt in your mind. – Naman Nov 20 '19 at 02:55
  • Possible duplicate of [Bridges in a connected graph](https://stackoverflow.com/questions/11218746/bridges-in-a-connected-graph) – Naman Nov 20 '19 at 02:56

2 Answers2

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);
  }
}

Reference: https://github.com/williamfiset/Algorithms/blob/master/com/williamfiset/algorithms/graphtheory/ArticulationPointsAdjacencyList.java

James Bond
  • 156
  • 6
1

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:

Tarjan Algorithm

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.

Algorithm description

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

Example

(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]

Code

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);
}
Marco Zamboni
  • 204
  • 1
  • 9