package com.interview.graph;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Date 08/22/2015
* @author Tushar Roy
*
* Find articulation points in connected undirected graph.
* Articulation points are vertices such that removing any one of them disconnects the graph.
*
* We need to do DFS of this graph and keep visitedTime and lowTime for each vertex.
* lowTime is keeps track of back edges.
*
* If any one of following condition meets then vertex is articulation point.
*
* 1) If vertex is root of DFS and has atlesat 2 independent children.(By independent it means they are
* not connected to each other except via this vertex). This condition is needed because if we
* started from corner vertex it will meet condition 2 but still is not an articulation point. To filter
* out those vertices we need this condition.
*
* 2) It is not root of DFS and if visitedTime of vertex <= lowTime of any adjacent vertex then its articulation point.
*
* Time complexity is O(E + V)
* Space complexity is O(V)
*
* References:
* https://en.wikipedia.org/wiki/Biconnected_component
* http://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/
*/
public class ArticulationPoint<T> {
private int time;
public Set<Vertex<T>> findarticulationPoints(Graph<T> graph) {
time = 0;
Set<Vertex<T>> visited = new HashSet<>();
Set<Vertex<T>> articulationPoints = new HashSet<>();
Vertex<T> startVertex = graph.getAllVertex().iterator().next();
Map<Vertex<T>, Integer> visitedTime = new HashMap<>();
Map<Vertex<T>, Integer> lowTime = new HashMap<>();
Map<Vertex<T>, Vertex<T>> parent = new HashMap<>();
DFS(visited,articulationPoints,startVertex, visitedTime, lowTime, parent);
return articulationPoints;
}
private void DFS(Set<Vertex<T>> visited,
Set<Vertex<T>> articulationPoints, Vertex<T> vertex,
Map<Vertex<T>, Integer> visitedTime,
Map<Vertex<T>, Integer> lowTime, Map<Vertex<T>, Vertex<T>> parent) {
visited.add(vertex);
visitedTime.put(vertex, time);
lowTime.put(vertex, time);
time++;
int childCount =0;
boolean isArticulationPoint = false;
for(Vertex<T> adj : vertex.getAdjacentVertexes()){
//if adj is same as parent then just ignore this vertex.
if(adj.equals(parent.get(vertex))) {
continue;
}
//if adj has not been visited then visit it.
if(!visited.contains(adj)) {
parent.put(adj, vertex);
childCount++;
DFS(visited, articulationPoints, adj, visitedTime, lowTime, parent);
if(visitedTime.get(vertex) <= lowTime.get(adj)) {
isArticulationPoint = true;
} else {
//below operation basically does lowTime[vertex] = min(lowTime[vertex], lowTime[adj]);
lowTime.compute(vertex, (currentVertex, time) ->
Math.min(time, lowTime.get(adj))
);
}
} else { //if adj is already visited see if you can get better low time.
//below operation basically does lowTime[vertex] = min(lowTime[vertex], visitedTime[adj]);
lowTime.compute(vertex, (currentVertex, time) ->
Math.min(time, visitedTime.get(adj))
);
}
}
//checks if either condition 1 or condition 2 meets). If yes then it is articulation point.
if((parent.get(vertex) == null && childCount >= 2) || parent.get(vertex) != null && isArticulationPoint ) {
articulationPoints.add(vertex);
}
}
public static void main(String args[]){
Graph<Integer> graph = new Graph<>(false);
graph.addEdge(1, 2);
graph.addEdge(2, 3);
graph.addEdge(1, 3);
graph.addEdge(1, 4);
graph.addEdge(4, 5);
graph.addEdge(5, 6);
graph.addEdge(6, 7);
graph.addEdge(7, 5);
graph.addEdge(6, 8);
//bigger example
/*
graph.addEdge(0, 1);
graph.addEdge(0, 2);
graph.addEdge(0, 3);
graph.addEdge(0, 4);
graph.addEdge(4, 2);
graph.addEdge(3, 5);
graph.addEdge(4, 6);
graph.addEdge(6, 3);
graph.addEdge(6, 7);
graph.addEdge(6, 8);
graph.addEdge(7, 9);
graph.addEdge(9, 10);
graph.addEdge(8, 10);*/
ArticulationPoint<Integer> ap = new ArticulationPoint<Integer>();
Set<Vertex<Integer>> aPoints = ap.findarticulationPoints(graph);
aPoints.forEach(point -> System.out.println(point));
}
}