Finding all the backedges in an undirected graph
Finding cycles in a graph is an interesting problem. We say that a cycle is formed if there is a way to reach the source without visiting already visited vertices.
In this article I will discuss about finding all the backedges in an undirected graph so that we can determine how many cycles are present in the graph. If there are no backedges in a graph it means that there can’t be any cycles in that graph.
For example in the above diagram, the edge between B and F forms one backedge.
public class CycleDetection {
private final Graph<Node> graph;
private static final String GREY_COLOR = "grey";
public CycleDetection(Graph<Node> graph) {
super();
this.graph = graph;
}
public static void main(String[] args) {
Node one = new Node("A");
Node two = new Node("B");
Node three = new Node("C");
Node four = new Node("D");
Node five = new Node("E");
Node six = new Node("F");
Graph<Node> graph = new Graph<Node>();
graph.addNode(one);
graph.addNode(two);
graph.addNode(three);
graph.addNode(four);
graph.addNode(five);
graph.addNode(six);
graph.addEdge(one, two);
graph.addEdge(one, three);
graph.addEdge(one, four);
graph.addEdge(two, five);
graph.addEdge(two, six);
graph.addEdge(three, six);
graph.addEdge(two, one);
graph.addEdge(three, one);
graph.addEdge(four, one);
graph.addEdge(five, two);
graph.addEdge(six, two);
graph.addEdge(six, three);
CycleDetection detection = new CycleDetection(graph);
detection.detectCycles(six);
}
public void detectCycles(Node source) {
final Deque<Node> stack = new ArrayDeque<Node>();
final Set<Node> visited = new LinkedHashSet<Node>();
stack.push(source);
while (!stack.isEmpty()) {
Node node = stack.pop();
node.color = GREY_COLOR;
visited.add(node);
Set<Node> neighbours = this.graph.edgesFrom(node);
for (Node neighbour : neighbours) {
if (!GREY_COLOR.equals(neighbour.color)) {
if (stack.contains(neighbour)) {
System.out.println("Edge (" + node + ", " + neighbour + ") forms a " +
"backedge");
System.out.println("Visited " + visited);
}
stack.push(neighbour);
}
}
}
}
}
//Thanks to Keith Schwarz
class Graph<T> {
private final Map<T, Set<T>> graph;
public Graph() {
super();
this.graph = new HashMap<T, Set<T>>();
}
public Set<T> getNodes() {
return Collections.unmodifiableSet(this.graph.keySet());
}
public Set<T> edgesFrom(T node) {
Set<T> adjacentNodes = this.graph.get(node);
if (adjacentNodes == null) {
throw new NoSuchElementException("Node doesn't exist in the graph");
}
return Collections.unmodifiableSet(adjacentNodes);
}
// Create a default empty edge set
public boolean addNode(T node) {
if (node == null) {
throw new IllegalArgumentException("Node can't be null");
}
if (!this.graph.containsKey(node)) {
this.graph.put(node, new HashSet<T>());
}
return true;
}
// Removes all the associated edges of this node
public void removeNode(T node) {
if (node == null) {
throw new IllegalArgumentException("Node can't be null");
}
if (this.graph.containsKey(node)) {
this.graph.remove(node);
}
}
public void addEdge(T one, T two) {
requireNonNullAndGraphContains(one, two);
this.graph.get(one).add(two);
}
public void removeEdge(T one, T two) {
requireNonNullAndGraphContains(one, two);
this.graph.get(one).remove(two);
}
public boolean edgeExists(T one, T two) {
requireNonNullAndGraphContains(one, two);
return this.graph.get(one).contains(two);
}
private void requireNonNullAndGraphContains(T one, T two) {
if (one == null || two == null) {
throw new IllegalArgumentException("One or both of the arguments are null.");
}
if (!this.graph.containsKey(one) || !this.graph.containsKey(two)) {
throw new IllegalArgumentException("One or both of the arguments are not part of the " +
"" + "" + "graph");
}
}
public int size() {
return this.graph.size();
}
@Override
public String toString() {
return this.graph.toString();
}
}
class Node implements Comparable<Node> {
private final String name;
String color;
private double weight = 0;// Default node weight is zero
public Node(String name) {
super();
this.name = name;
this.color = "white";
}
public String getName() {
return name;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public int compareTo(Node other) {
return Double.compare(other.weight, this.weight);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(weight);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null) {
return false;
}
if (getClass() != object.getClass()) {
return false;
}
Node other = (Node) object;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (Double.doubleToLongBits(weight) != Double.doubleToLongBits(other.weight)) {
return false;
}
return true;
}
@Override
public String toString() {
return name;
}
}
Hope you have followed the self explanatory code…
Thanks to Janos(http://codereview.stackexchange.com/users/12390/janos) – For his insights on improving the code
–Rajasekhar
Helical IT Solutions
Best Open Source Business Intelligence Software Helical Insight is Here

