Pages

Friday, 2 May 2014

Graph Implementation in Java

A graph data structure consists of a finite (and possibly mutable) set of ordered pairs, called edges or arcs, of certain entities called nodes or vertices. As in mathematics, an edge (x,y) is said to point or go from x to y. A graph data structure may also associate to each edge some edge value, such as a symbolic label or a numeric attribute (cost, capacity, length, etc.).


Vocabulary and Definitions

Vertex

A vertex (also called a “node”) is a fundamental part of a graph. It can have a name, which we will call the “key.” A vertex may also have additional information. We will call this additional information the “payload.”

Edge

An edge (also called an “arc”) is another fundamental part of a graph. An edge connects two vertices to show that there is a relationship between them. Edges may be one-way or two-way. If the edges in a graph are all one-way, we say that the graph is a directed graph, or a digraph.

Weight

Edges may be weighted to show that there is a cost to go from one vertex to another. For example in a graph of roads that connect one city to another, the weight on the edge might represent the distance between the two cities.

Path

A path in a graph is a sequence of vertices that are connected by edges.

Cycle

A cycle in a directed graph is a path that starts and ends at the same vertex. 

Implementation

An Adjacency Matrix

One of the easiest ways to implement a graph is to use a two-dimensional matrix. In this matrix implementation, each of the rows and columns represent a vertex in the graph. The value that is stored in the cell at the intersection of row v and column w indicates if there is an edge from vertex v to vertex w. When two vertices are connected by an edge, we say that they are adjacent.
The advantage of the adjacency matrix is that it is simple, and for small graphs it is easy to see which nodes are connected to other nodes. However, notice that most of the cells in the matrix are empty. Because most of the cells are empty we say that this matrix is “sparse.” A matrix is not a very efficient way to store sparse data. 

An Adjacency List

A more space-efficient way to implement a sparsely connected graph is to use an adjacency list. In an adjacency list implementation we keep a master list of all the vertices in the Graph object and then each vertex object in the graph maintains a list of the other vertices that it is connected to. 
The advantage of the adjacency list implementation is that it allows us to compactly represent a sparse graph. The adjacency list also allows us to easily find all the links that are directly connected to a particular vertex.

Basic Graph Operations

Following code represents the implementation of Vertix for a graph in generics.
 public class Vertix<V> {  
      private V value;  
      private boolean visited;  
      public Vertix(V value) {  
           this.visited = false;  
           this.value = value;  
      }  
      public boolean isVisited() {  
           return visited;  
      }  
      public void setVisited(boolean visited) {  
           this.visited = visited;  
      }  
      public V getValue() {  
           return value;  
      }  
      public void setValue(V value) {  
           this.value = value;  
      }  
      @Override  
      public boolean equals(Object that) {  
           if (that == null) {  
                return false;  
           } else if (that instanceof Vertix) {  
                return (((Vertix<?>) that).getValue().equals(this.getValue()));  
           }  
           return false;  
      }  
      @Override  
      public String toString() {  
           return String.valueOf(this.getValue());  
      }  
 }  
Class AdjacencyListGraph represents the implementation of a graph with Adjacency List approach. We will add various functionalities to this class we we go on:
 import java.util.ArrayList;  
 import java.util.HashMap;  
 import java.util.List;  
 import java.util.Map;  
 import java.util.Set;  
 import uk.ac.soton.ecs.datastructure.queue.Queue;  
 public class AdjacencyListGraph<V> {  
      private Map<Vertix<V>, List<Vertix<V>>> adjacencyList;  
      public AdjacencyListGraph() {  
           this.adjacencyList = new HashMap<Vertix<V>, List<Vertix<V>>>();  
      }  
 }  

Adding a Vertex

      public Vertix<V> addVertex(V value) {  
           if (value == null) {  
                return null;  
           }  
           Vertix<V> newVertix = new Vertix<V>(value);  
           adjacencyList.put(newVertix, new ArrayList<Vertix<V>>());  
           return newVertix;  
      }  

Getting a Vertex

Provided that the input to this method (value) is unique in the graph. This method returns back the first occurrence of the vertex or null.
      public Vertix<V> getVertex(V value) {  
           if (value == null) {  
                return null;  
           }  
           for (Vertix<V> v : adjacencyList.keySet()) {  
                if (v.getValue().equals(value)) {  
                     return v;  
                }  
           }  
           return null;  
      }  

Adding an Edge

      public int addEdge(Vertix<V> from, Vertix<V> to) {  
           if (from == null || to == null) {  
                return -1;  
           }  
           if (!adjacencyList.containsKey(from) || !adjacencyList.containsKey(to)) {  
                return -1;  
           }  
           adjacencyList.get(from).add(to);  
           return 1;  
      }  

Checking if a graph has an edge

      public boolean hasEdge(Vertix<V> from, Vertix<V> to) {  
           if (from == null || to == null) {  
                return false;  
           }  
           if (!adjacencyList.containsKey(from) || !adjacencyList.containsKey(to)) {  
                return false;  
           }  
           for (Vertix<V> t : adjacencyList.get(from)) {  
                if (t.equals(to)) {  
                     return true;  
                }  
           }  
           return false;  
      }  

Retrieve all out edges from a vertex 

      public List<Vertix<V>> outEdges(Vertix<V> vertix) {  
           List<Vertix<V>> edges = new ArrayList<Vertix<V>>();  
           for (Vertix<V> to : adjacencyList.get(vertix)) {  
                edges.add(to);  
           }  
           return edges;  
      }  

Retrieve all in edges to a vertex 

      public List<Vertix<V>> inEdges(Vertix<V> vertix) {  
           Set<Vertix<V>> keys = adjacencyList.keySet();  
           List<Vertix<V>> edges = new ArrayList<Vertix<V>>();  
           for (Vertix<V> key : keys) {  
                if (adjacencyList.get(key).contains(vertix)) {  
                     edges.add(key);  
                }  
           }  
           return edges;  
      }  

Depth First Search from a vertex to another 

      public List<Vertix<V>> getDFSPath(Vertix<V> from, Vertix<V> to) {  
           resetVerticesVisibility();  
           List<Vertix<V>> DFSPath = new ArrayList<Vertix<V>>();  
           getDFSPath(from, to, DFSPath);  
           if (DFSPath.isEmpty()) {  
                return null;  
           } else {  
                return DFSPath;  
           }  
      }  
      private void getDFSPath(Vertix<V> from, Vertix<V> to,  
                List<Vertix<V>> DFSPath) {  
           DFSPath.add(from);  
           from.setVisited(true);  
           if (from.equals(to)) {  
                return;  
           }  
           for (Vertix<V> middle : adjacencyList.get(from)) {  
                if (!middle.isVisited()) {  
                     getDFSPath(middle, to, DFSPath);  
                }  
                if (DFSPath.get(DFSPath.size() - 1).equals(to)) {  
                     return;  
                }  
           }  
           DFSPath.remove(from);  
      }  

Breadth First Search from a vertex to another 

      public List<Vertix<V>> getBFSPath(Vertix<V> from, Vertix<V> to) {  
           resetVerticesVisibility();  
           List<Vertix<V>> BFSPath = new ArrayList<Vertix<V>>();  
           Queue<Vertix<V>> queue = new Queue<Vertix<V>>();  
           queue.enqueue(from);  
           BFSPath.add(from);  
           from.setVisited(true);  
           while (!queue.isEmpty()) {  
                Vertix<V> middle = queue.dequeue();  
                if (middle.equals(to)) {  
                     break;  
                }  
                for (Vertix<V> v : adjacencyList.get(middle)) {  
                     if (!v.isVisited()) {  
                          BFSPath.add(v);  
                          v.setVisited(true);  
                          queue.enqueue(v);  
                     }  
                }  
           }  
           return BFSPath;  
      }  

Evaluation

Complexity Analysis


Complexity of functionalities in Graphs
Functionality Complexity (Adjacency list) Complexity (Adjacency matrix)
Storage O(|V|+|E|) O(|V|^2)
Add vertex O(1) O(|V|^2)
Add edge O(1) O(1)
Remove vertex O(|E|) O(|V|^2)
Remove edge O(|E|) O(1)

Advantages

Disadvantages

Source Code

 public class Vertix<V> {  
      private V value;  
      private boolean visited;  
      public Vertix(V value) {  
           this.visited = false;  
           this.value = value;  
      }  
      public boolean isVisited() {  
           return visited;  
      }  
      public void setVisited(boolean visited) {  
           this.visited = visited;  
      }  
      public V getValue() {  
           return value;  
      }  
      public void setValue(V value) {  
           this.value = value;  
      }  
      @Override  
      public boolean equals(Object that) {  
           if (that == null) {  
                return false;  
           } else if (that instanceof Vertix) {  
                return (((Vertix<?>) that).getValue().equals(this.getValue()));  
           }  
           return false;  
      }  
      @Override  
      public String toString() {  
           return String.valueOf(this.getValue());  
      }  
 }  
 import java.util.ArrayList;  
 import java.util.HashMap;  
 import java.util.List;  
 import java.util.Map;  
 import java.util.Set;  
 import uk.ac.soton.ecs.datastructure.queue.Queue;  
 public class AdjacencyListGraph<V> {  
      private Map<Vertix<V>, List<Vertix<V>>> adjacencyList;  
      public AdjacencyListGraph() {  
           this.adjacencyList = new HashMap<Vertix<V>, List<Vertix<V>>>();  
      }  
      public Vertix<V> addVertex(V value) {  
           if (value == null) {  
                return null;  
           }  
           Vertix<V> newVertix = new Vertix<V>(value);  
           adjacencyList.put(newVertix, new ArrayList<Vertix<V>>());  
           return newVertix;  
      }  
      public int addEdge(Vertix<V> from, Vertix<V> to) {  
           if (from == null || to == null) {  
                return -1;  
           }  
           if (!adjacencyList.containsKey(from) || !adjacencyList.containsKey(to)) {  
                return -1;  
           }  
           adjacencyList.get(from).add(to);  
           return 1;  
      }  
      public boolean hasEdge(Vertix<V> from, Vertix<V> to) {  
           if (from == null || to == null) {  
                return false;  
           }  
           if (!adjacencyList.containsKey(from) || !adjacencyList.containsKey(to)) {  
                return false;  
           }  
           for (Vertix<V> t : adjacencyList.get(from)) {  
                if (t.equals(to)) {  
                     return true;  
                }  
           }  
           return false;  
      }  
      public List<Vertix<V>> outEdges(Vertix<V> vertix) {  
           List<Vertix<V>> edges = new ArrayList<Vertix<V>>();  
           for (Vertix<V> to : adjacencyList.get(vertix)) {  
                edges.add(to);  
           }  
           return edges;  
      }  
      public List<Vertix<V>> inEdges(Vertix<V> vertix) {  
           Set<Vertix<V>> keys = adjacencyList.keySet();  
           List<Vertix<V>> edges = new ArrayList<Vertix<V>>();  
           for (Vertix<V> key : keys) {  
                if (adjacencyList.get(key).contains(vertix)) {  
                     edges.add(key);  
                }  
           }  
           return edges;  
      }  
      public List<Vertix<V>> getDFSPath(Vertix<V> from, Vertix<V> to) {  
           resetVerticesVisibility();  
           List<Vertix<V>> DFSPath = new ArrayList<Vertix<V>>();  
           getDFSPath(from, to, DFSPath);  
           if (DFSPath.isEmpty()) {  
                return null;  
           } else {  
                return DFSPath;  
           }  
      }  
      public List<Vertix<V>> getBFSPath(Vertix<V> from, Vertix<V> to) {  
           resetVerticesVisibility();  
           List<Vertix<V>> BFSPath = new ArrayList<Vertix<V>>();  
           Queue<Vertix<V>> queue = new Queue<Vertix<V>>();  
           queue.enqueue(from);  
           BFSPath.add(from);  
           from.setVisited(true);  
           while (!queue.isEmpty()) {  
                Vertix<V> middle = queue.dequeue();  
                if (middle.equals(to)) {  
                     break;  
                }  
                for (Vertix<V> v : adjacencyList.get(middle)) {  
                     if (!v.isVisited()) {  
                          BFSPath.add(v);  
                          v.setVisited(true);  
                          queue.enqueue(v);  
                     }  
                }  
           }  
           return BFSPath;  
      }  
      private void getDFSPath(Vertix<V> from, Vertix<V> to,  
                List<Vertix<V>> DFSPath) {  
           DFSPath.add(from);  
           from.setVisited(true);  
           if (from.equals(to)) {  
                return;  
           }  
           for (Vertix<V> middle : adjacencyList.get(from)) {  
                if (!middle.isVisited()) {  
                     getDFSPath(middle, to, DFSPath);  
                }  
                if (DFSPath.get(DFSPath.size() - 1).equals(to)) {  
                     return;  
                }  
           }  
           DFSPath.remove(from);  
      }  
      private void resetVerticesVisibility() {  
           for (Vertix<V> vertix : adjacencyList.keySet()) {  
                vertix.setVisited(false);  
           }  
      }  
      @Override  
      public String toString() {  
           Set<Vertix<V>> keys = adjacencyList.keySet();  
           String str = "";  
           for (Vertix<V> v : keys) {  
                str += v.getValue() + ": ";  
                List<Vertix<V>> edgeList = adjacencyList.get(v);  
                for (Vertix<V> edge : edgeList) {  
                     str += edge.getValue() + " ";  
                }  
                str += "\n";  
           }  
           return str;  
      }  
 }  

JUnit Test

 import static org.junit.Assert.assertTrue;  
 import java.util.List;  
 import org.junit.Test;  
 public class AdjacencyListGraphTest {  
      @Test  
      public void testAdjacencyListGraph() {  
           AdjacencyListGraph<String> names = new AdjacencyListGraph<String>();  
           Vertix<String> v1 = names.addVertex("v1");  
           Vertix<String> v2 = names.addVertex("v2");  
           Vertix<String> v3 = names.addVertex("v3");  
           Vertix<String> v4 = names.addVertex("v4");  
           Vertix<String> v5 = names.addVertex("v5");  
           Vertix<String> v6 = names.addVertex("v6");  
           Vertix<String> v7 = names.addVertex("v7");  
           names.addEdge(v3, v1);  
           names.addEdge(v3, v2);  
           names.addEdge(v4, v1);  
           names.addEdge(v4, v2);  
           names.addEdge(v5, v1);  
           names.addEdge(v5, v2);  
           names.addEdge(v6, v4);  
           names.addEdge(v7, v4);  
           System.out.println("DFS:");  
           List<Vertix<String>> DFS = names.getDFSPath(v7, v1);  
           for (Vertix<String> p : DFS) {  
                System.out.print(p.toString() + "->");  
           }  
           System.out.println();  
           System.out.println("BFS:");  
           List<Vertix<String>> BFS = names.getBFSPath(v7, v1);  
           for (Vertix<String> p : BFS) {  
                System.out.print(p.toString() + "->");  
           }  
           System.out.println();  
           assertTrue(names.hasEdge(v3, v1));  
           assertTrue(3 == names.inEdges(v1).size());  
           assertTrue(2 == names.outEdges(v4).size());  
           System.out.println(names.toString());  
      }  
 }  

References

No comments:

Post a Comment