Java Graph Tutorial-How to implement graph data structure

This comprehensive Java Graph tutorial explains the Graph data structure in detail. It includes how to create, implement, represent, and traverse graphs in Java:

The graph data structure mainly represents a network connecting various points. These points are called vertices, and the links connecting these vertices are called "edges". Therefore, graph g is defined as a set of vertices V and edges E connecting these vertices.

Diagrams are mainly used to represent various networks, such as computer networks, social networks, etc. They can also be used to represent various dependencies in software or architecture. These dependency graphs are very useful for analyzing software and sometimes debugging it.

 

Java graph data structure

The graph given below has five vertices {A, B, C, D, E}, and its edges are given by { {AB}, {AC}, {AD}, {BD}, {CE}, {ED}} Out. Since the edge does not show any direction, this graph is called an "undirected graph".

Undirected graph

Undirected graph

In addition to the undirected graph shown above, there are several graph variants in Java.

Let us discuss these variants in detail.

Different variants of the graph

Here are some variations of this diagram.

#1) Directed graph

A directed graph or directed graph is a graph data structure in which the edges have a specific direction. They originate from one vertex and eventually reach another vertex.

The following figure shows an example of a directed graph.

Directed graph

Directed graph

In the above figure, there is an edge from vertex A to vertex B. Note, however, that unless there is an edge from B to A, A to B is like B to A in an undirected graph.

If there is at least one path where the first and last vertices are the same, the directed graph is cyclic. In the above figure, the path A-> B-> C-> D-> E-> A forms a directed ring or cyclic graph.

On the contrary, a directed acyclic graph is a graph in which there is no directed ring, that is, there is no path forming a ring.

#2) Weighted graph

In a weighted graph, the weight is associated with each edge of the graph. The weight usually represents the distance between two vertices. The figure below shows the weighted graph. Since the direction is not shown, this is an undirected graph.

Weighted graph

Weighted graph

Note that the weighted graph can be directed or undirected.

How to create graphics?

Java does not provide a complete implementation of graphical data structures. However, we can use Collections in Java to represent graphics programmatically. We can also use dynamic arrays like vectors to implement graphs.

Usually, we use HashMap collections to implement graphics in Java. HashMap elements take the form of key-value pairs. We can represent graph adjacency list in HashMap.

The most common way to create a graph is to use one of the representations of the graph, such as an adjacency matrix or adjacency list. Next, we will discuss these representations and then implement graphs in Java using adjacency lists that will use ArrayList.

Graphical representation in Java

Graphic representation refers to the method or technology of storing graphic data in computer memory.

We have two main graphical representations as shown below.

Adjacency matrix

The adjacency matrix is ​​a linear representation of the graph. This matrix stores the mapping of the vertices and edges of the graph. In the adjacency matrix, the vertices of the graph represent rows and columns. This means that if the graph has N vertices, the size of the adjacency matrix will be NxN.

If V is a set of vertices of the graph, the intersection point M ij = 1 in the adjacency list indicates that there is an edge between vertices i and j.

In order to understand this concept better, let us prepare an adjacency matrix for undirected graphs.

Adjacency matrix

As can be seen from the figure above, for vertex A, the intersections AB and AE are set to 1, because there are edges from A to B and A to E. Similarly, the intersection point BA is set to 1, because this is an undirected graph, AB = BA. Similarly, we set all other intersections with edges to 1.

In the case of a directed graph, only when there is a clear edge from Vi to Vj, the intersection Mij will be set to 1.

As shown below.

Adjacency matrix-directed graph

As you can see from the figure above, there is an edge from A to B. Therefore, the intersection AB is set to 1, and the intersection BA is set to 0. This is because there is no side from B to A.

Consider vertices E and D. We see that there are edges from E to D and from D to E. Therefore, we set these two intersection points to 1 in the adjacency matrix.

Now we continue with the weighted graph. As we know about weighted graphs, each edge is associated with an integer, also known as weight. We represent this weight for existing edges in the adjacency matrix. As long as there is an edge from one vertex to another instead of "1", assign the weight.

This representation is shown below.

Adjacency matrix-directed weighted graph

Adjacency list

In addition to representing the graph as an adjacency matrix that is continuous in nature, we can also use link representation. This form of link representation is called an adjacency list. The adjacency list is just a linked list, and each node in the list represents a vertex.

The pointer from the first vertex to the second vertex indicates that there is an edge between the two vertices. Maintain this adjacency list for each vertex in the graph.

When we traverse all neighboring nodes of a particular node, we store NULL in the next pointer field of the last node of the adjacency table.

Now, we will use the graph used to represent the adjacency matrix above to demonstrate the adjacency list.

Show the adjacency list.

The figure above shows the adjacency list of an undirected graph. We see that every vertex or node has its adjacency list.

For undirected graphs, the total length of the adjacency list is usually twice the number of sides. In the above figure, the total number of edges is 6, and the sum of the lengths of all adjacency lists is 12.

Now, let us prepare an adjacency list for the directed graph.

Total length of adjacency

As can be seen from the figure above, in a directed graph, the total length of the adjacency list of the graph is equal to the number of edges in the graph. In the above figure, there are 9 edges, and the sum of the lengths of the adjacency list in this figure = 9.

Now let us consider the following weighted directed graph. Note that each edge of the weighted graph has a weight associated with it. Therefore, when we use the adjacency list to represent the graph, we must add a new field to each list node to represent the weight of the edge.

The adjacency list of the weighted graph is shown below.

Weighted graph

The figure above shows the weighted graph and its adjacency list. Note that there is a new space in the adjacency list, indicating the weight of each node.

Graph implementation in Java

The following program shows the implementation of Java graphics. Here, we use an adjacency list to represent the graph.

package Graph;

import java.util.*;

//class to store edges of the weighted graph
class Edge {
	int src, dest, weight;

	Edge(int src, int dest, int weight) {
		this.src = src;
		this.dest = dest;
		this.weight = weight;
	}
}

//Graph class
class Graph {
	// node of adjacency list
	static class Node {
		int value, weight;

		Node(int value, int weight) {
			this.value = value;
			this.weight = weight;
		}
	};

//define adjacency list

	List<List<Node>> adj_list = new ArrayList<>();

	// Graph Constructor
	public Graph(List<Edge> edges) {
		// adjacency list memory allocation
		for (int i = 0; i < edges.size(); i++)
			adj_list.add(i, new ArrayList<>());

		// add edges to the graph
		for (Edge e : edges) {
			// allocate new node in adjacency List from src to dest
			adj_list.get(e.src).add(new Node(e.dest, e.weight));
		}
	}

//print adjacency list for the graph
	public static void printGraph(Graph graph) {
		int src_vertex = 0;
		int list_size = graph.adj_list.size();

		System.out.println("The contents of the graph:");
		while (src_vertex < list_size) {
			// traverse through the adjacency list and print the edges
			for (Node edge : graph.adj_list.get(src_vertex)) {
				System.out.print("Vertex:" + src_vertex + " ==> " + edge.value + " (" + edge.weight + ")\t");
			}

			System.out.println();
			src_vertex++;
		}
	}
}

class Main {
	public static void main(String[] args) {
		// define edges of the graph
		List<Edge> edges = Arrays.asList(new Edge(0, 1, 2), new Edge(0, 2, 4), new Edge(1, 2, 4), new Edge(2, 0, 5),
				new Edge(2, 1, 4), new Edge(3, 2, 3), new Edge(4, 5, 1), new Edge(5, 4, 3));

		// call graph class Constructor to construct a graph
		Graph graph = new Graph(edges);

		// print the graph as an adjacency list
		Graph.printGraph(graph);
	}
}

Output:

Output

Graph traversal Java

In order to perform any meaningful operation (such as searching whether there is any data), we need to traverse the graph so that each vertex and edge of the graph is accessed at least once. This is done by using graphics algorithms, which are just a set of instructions that help us traverse the graph.

Two algorithms are supported to traverse the graph in Java .

  1. Depth-first traversal
  2. Breadth first traversal

Depth-first traversal

Depth First Search (DFS) is a technique used to traverse trees or graphs. The DFS technology starts from the root node, and then traverses the adjacent nodes of the root node by traversing the graph more deeply. In DFS technology, nodes are traversed in the depth direction until there are no more children to explore.

Once we reach the leaf node (no more child nodes), DFS will backtrack and start from other nodes and traverse in a similar way. DFS technology uses a stack data structure to store the nodes being traversed.

The following is the algorithm of DFS technology.

algorithm

Step 1: Start from the root node and insert it into the stack

Step 2: Pop the item from the stack and insert the "visited" list

Step 3: For the node marked as "visited" (or visited list), add adjacent nodes of the node that have not been marked as visited to the stack.

Step 4: Repeat steps 2 and 3 until the stack is empty.

Illustration of DFS technology

Now, we will use appropriate graphical examples to illustrate DFS technology.

Given below is an example diagram. We maintain a stack to store explored nodes and a list to store visited nodes.

DFS technology

We will start with A, mark it as visited, and add it to the visited list. Then, we will consider all adjacent nodes of A and push these nodes onto the stack as shown below.

A node in the stack

Next, we pop a node from the stack, namely B and mark it as visited. Then, add it to the "Visited" list. As follows.

Adjacent node

Now we consider the neighboring nodes of B, namely A and C. Have already visited A among them. Therefore, we ignore it. Next, we pop C from the stack. Mark C as visited. The adjacent node of C, that is, E is added to the stack.

Next node

Next, we pop the next node E from the stack and mark it as visited. The neighbor of node E is C, which has been visited. Therefore, we ignore it.

D remains in the stack

Now, only node D remains in the stack. Therefore, we mark it as visited. Its neighbor node is A, which has already been visited. Therefore, we do not add it to the stack.

Depth-first traversal

At this time, the stack is empty. This means that we have completed the depth-first traversal of the given graph.

The access list uses depth-first technology to give the final order of traversal. The final DFS sequence in the figure above is A-> B-> C-> E-> D.

DFS implementation

package GraphDFS;

import java.io.*;
import java.util.*;

//DFS Technique for undirected graph
class Graph {
	private int Vertices; // No. of vertices

	// adjacency list declaration
	private LinkedList<Integer> adj_list[];

	// graph Constructor: to initialize adjacency lists as per no of vertices
	Graph(int v) {
		Vertices = v;
		adj_list = new LinkedList[v];
		for (int i = 0; i < v; ++i)
			adj_list[i] = new LinkedList();
	}

	// add an edge to the graph
	void addEdge(int v, int w) {
		adj_list[v].add(w); // Add w to v's list.
	}

	// helper function for DFS technique
	void DFS_helper(int v, boolean visited[]) {
		// current node is visited
		visited[v] = true;
		System.out.print(v + " ");

		// process all adjacent vertices
		Iterator<Integer> i = adj_list[v].listIterator();
		while (i.hasNext()) {
			int n = i.next();
			if (!visited[n])
				DFS_helper(n, visited);
		}
	}

	void DFS(int v) {
		// initially none of the vertices are visited
		boolean visited[] = new boolean[Vertices];

		// call recursive DFS_helper function for DFS technique
		DFS_helper(v, visited);
	}
}

class Main {
	public static void main(String args[]) {
		// create a graph object and add edges to it
		Graph g = new Graph(5);
		g.addEdge(0, 1);
		g.addEdge(0, 2);
		g.addEdge(0, 3);
		g.addEdge(1, 2);
		g.addEdge(2, 4);
		// print the DFS Traversal sequence
		System.out.println("Depth First Traversal for given graph" + "(with 0 as starting vertex)");
		g.DFS(0);
	}
}

Output:

Output

Application of DFS

#1) Detect cycles in the graph: When we can go back to the edge, DFS helps to detect cycles in the graph.

#2) Pathfinding: As we have seen in the DFS illustration, given any two vertices, we can find the path between these two vertices.

#3) Minimum  spanning tree and shortest path: If we run DFS technology on a non-weighted graph, it will provide us with the minimum spanning tree and shortest path.

#4) Topological sorting: When we have to schedule homework, use topological sorting. We have dependencies between various jobs. We can also use topological sorting to resolve the dependencies between linkers, instruction schedulers, data serialization, etc.

Breadth first traversal

Breadth First (BFS) technology uses queues to store the nodes of the graph. Unlike DFS technology, in BFS, we traverse the graph horizontally. This means that we traverse the graph wisely. When we explore all vertices or nodes on one level, we will move to the next level.

Given below is the algorithm of breadth-first traversal technology .

algorithm

Let us look at the algorithm of BFS technology.

Given a graph G, we need to perform BFS technology on it.

  • Step 1: Start from the root node and insert it into the queue.
  • Step 2: Repeat steps 3 and 4 for all nodes in the graph.
  • Step 3: Remove the root node from the queue and add it to the Visited list.
  • Step 4:  Now add all adjacent nodes of the root node to the queue, and repeat steps 2 to 4 for each node. [END OF LOOP]
  • Step 6:  Exit

Illustration of BFS

Let us use the example diagram shown below to illustrate the BFS technology. Please note that we maintain a list called "Visit" and a queue. For clarity, we use the same graph used in the DFS example.

Illustration of BFS

First, we start from the root, node A, and add it to the access list. All adjacent nodes of node A (ie, B, C, and D) are added to the queue.

Visit the list and mark

Next, we remove node B from the queue. We add it to the "visited" list and mark it as "visited". Next, we explore the neighboring nodes of B in the queue (C is already in the queue). Another adjacent node A has already been visited, so we ignore it.

Visit the list and mark

Next, we remove node C from the queue and mark it as visited. We add C to the visit list, and add its neighboring node E to the queue.

Queue and mark

Next, we remove D from the queue and mark it as visited. The neighbor node A of node D has been visited, so we ignore it.

Adjacent node A of D

Therefore, now only node E is in the queue. We mark it as visited and add it to the visited list. The neighboring node of E is C, which has been visited. Therefore, please ignore it.

Results of BFS traversal

At this point, the queue is empty, and the access list has the sequence we obtained through BFS traversal. The order is A-> B-> C-> D-> E.

BFS implementation

The following Java program shows the implementation of BFS technology.

package GraphBFS;

import java.io.*;
import java.util.*;

//undirected graph represented using adjacency list.  
class Graph {
	private int Vertices; // No. of vertices
	private LinkedList<Integer> adj_list[]; // Adjacency Lists

	// graph Constructor:number of vertices in graph are passed
	Graph(int v) {
		Vertices = v;
		adj_list = new LinkedList[v];
		for (int i = 0; i < v; ++i) // create adjacency lists
			adj_list[i] = new LinkedList();
	}

	// add an edge to the graph
	void addEdge(int v, int w) {
		adj_list[v].add(w);
	}

	// BFS traversal from the root_node
	void BFS(int root_node) {
		// initially all vertices are not visited
		boolean visited[] = new boolean[Vertices];

		// BFS queue
		LinkedList<Integer> queue = new LinkedList<Integer>();

		// current node = visited, insert into queue
		visited[root_node] = true;
		queue.add(root_node);

		while (queue.size() != 0) {
			// deque an entry from queue and process it
			root_node = queue.poll();
			System.out.print(root_node + " ");

			// get all adjacent nodes of current node and process
			Iterator<Integer> i = adj_list[root_node].listIterator();
			while (i.hasNext()) {
				int n = i.next();
				if (!visited[n]) {
					visited[n] = true;
					queue.add(n);
				}
			}
		}
	}
}

class Main {
	public static void main(String args[]) {
		// create a graph with 5 vertices
		Graph g = new Graph(5);
		// add edges to the graph
		g.addEdge(0, 1);
		g.addEdge(0, 2);
		g.addEdge(0, 3);
		g.addEdge(1, 2);
		g.addEdge(2, 4);
		// print BFS sequence
		System.out.println("Breadth-first traversal of graph with 0 as starting vertex:");
		g.BFS(0);
	}
}

Output:

Output

Application of BFS traversal

#1) Garbage collection: One of the algorithms used by garbage collection technology to replicate garbage collection is the "Cheney Algorithm". The algorithm uses breadth-first traversal technology.

# 2) Network Broadcast : using techniques BFS data packets from the network a point to point broadcast.

#3) GPS navigation: When using GPS navigation, we can use BFS technology to find adjacent nodes.

#4) Social networking sites:  BFS technology is also used in social networking sites to find the interpersonal network around a specific person.

#5) Shortest path and minimum spanning tree in unweighted graph : In unweighted graph, BFS technology can be used to find the shortest path between the minimum spanning tree and nodes.

Java graphics library

Java does not force programmers to always implement graphics in their programs. Java provides a lot of ready-made libraries, you can use them directly to use the graphs in the program. These libraries have all the graphics API functions required to make full use of graphics and their various functions.

Given below is a brief introduction to some graphics libraries in Java.

#1) Google Guava:  Google Guava provides a rich library that supports graphs and algorithms, including simple graphs, networks, value graphs, etc.

#2) Apache Commons:  Apache Commons is an Apache project that provides Graph data structure components and APIs, which operate on the graph data structure. These components are reusable.

#3) JGraphT:  JGraphT is one of the widely used Java graph libraries. It provides graph data structure functions, including simple graphs, directed graphs, weighted graphs, etc., as well as algorithms and APIs that work on graph data structures.

#4) SourceForge JUNG:  JUNG stands for "Java Universal Network/Graphics" and is a Java framework. JUNG provides an extensible language for analyzing, visualizing and modeling the data we want to represent as graphics.

JUNG also provides various algorithms and routines for decomposition, clustering, optimization, etc.

Frequently asked questions

Q#1) What is a Java graph?

Answer: The graph data structure mainly stores the connected data, for example, the network of people or the network of cities. Graph data structures are usually composed of nodes or points called vertices. Each vertex is connected to another vertex using links called edges.

Q#2) What is the type of graph?

Answer: The different types of diagrams are listed below.

  1. Line chart: A line chart is used to plot the change of a specific attribute with respect to time.
  2. Bar graph: The bar graph compares the values ​​of entities, such as the population of each city, the national literacy rate, etc.

In addition to these main types, we also have other types, such as pictograms, histograms, area charts, scatter charts, etc.

Q#3) What is a connected graph?

Answer: A connected graph is a graph in which each vertex is connected to another vertex. Therefore, in the connection graph, we can reach every vertex from every other vertex.

Q#4) What is the application of graphs?

Answer: Graphics can be used in a variety of applications. This diagram can be used to represent a complex network. Graphics are also used in social networking applications to represent networks of people and applications such as finding neighbors or contacts.

Graphics are used to represent the calculation process in computer science.

Question #5) How to store graphics?

Answer: There are three ways to store graphics in memory:

#1) We can store nodes or vertices as objects and edges as pointers.

#2) We can also store the graph as an adjacency matrix with the same number of rows and columns as the vertices. The intersection of each row and column indicates the presence or absence of an edge. In a non-weighted graph, the presence of an edge is represented by 1, while in a weighted graph, the presence of an edge is replaced by the weight of the edge.

#3) The last way to store a graph is to use an adjacency list of graph vertices or edges between nodes. Each node or vertex has its adjacency list.

in conclusion

In this tutorial, we discussed graphics in Java in detail. We explored various types of graphs, graph implementation and traversal techniques. You can use a graph to find the shortest path between nodes.

Guess you like

Origin blog.csdn.net/allway2/article/details/115034476