Chapter 38: Graph (Part 4)

In this chapter, we dive deeper into graph algorithms and explore some of the most fundamental techniques used in solving real-world problems. Graphs are an essential part of computer science, and understanding how to efficiently traverse and optimize paths in graphs is critical. This chapter focuses on key algorithms related to shortest paths and minimum spanning trees (MST), specifically the Bellman-Ford Algorithm and Prim's Algorithm.
1. Bellman-Ford Algorithm
The Bellman-Ford Algorithm is a well-known algorithm for finding the shortest paths from a single source vertex to all other vertices in a weighted graph. What sets this algorithm apart is its ability to handle graphs with negative weight edges. Unlike Dijkstra's algorithm, Bellman-Ford can detect negative weight cycles, making it highly versatile in solving complex graph problems.
The algorithm works by repeatedly relaxing the edges of the graph. It ensures that the shortest path to each vertex is progressively refined until it converges to the correct value. With a time complexity of (O(V \times E)), where (V) is the number of vertices and (E) is the number of edges, Bellman-Ford is slower than Dijkstra's algorithm but more powerful due to its handling of negative edges.
2. Bellman-Ford Algorithm (Code)
Let’s break down the code implementation for the Bellman-Ford Algorithm in Java. The key operations here include initializing the distances to infinity, relaxing the edges repeatedly, and checking for negative weight cycles.
import java.util.Arrays;
class BellmanFord {
static void bellmanFord(int[][] graph, int V, int E, int src) {
// Initialize distance array with a large value (infinity)
int[] dist = new int[V];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src] = 0;
// Relax all edges V-1 times
for (int i = 1; i < V; ++i) {
for (int j = 0; j < E; ++j) {
int u = graph[j][0];
int v = graph[j][1];
int weight = graph[j][2];
if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}
}
// Check for negative weight cycles
for (int j = 0; j < E; ++j) {
int u = graph[j][0];
int v = graph[j][1];
int weight = graph[j][2];
if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {
System.out.println("Graph contains negative weight cycle");
return;
}
}
// Print the calculated shortest distances
System.out.println("Vertex Distance from Source:");
for (int i = 0; i < V; ++i) {
System.out.println(i + "\t\t" + dist[i]);
}
}
public static void main(String[] args) {
int V = 5, E = 8;
int graph[][] = { { 0, 1, -1 }, { 0, 2, 4 }, { 1, 2, 3 },
{ 1, 3, 2 }, { 1, 4, 2 }, { 3, 2, 5 },
{ 3, 1, 1 }, { 4, 3, -3 } };
bellmanFord(graph, V, E, 0);
}
}
In this implementation:
Graph Representation: The graph is represented as an array of edges, where each edge is represented by a triplet (u, v, w) — meaning there’s an edge from vertex u to vertex v with weight w.
Relaxation: Each edge is relaxed V-1 times, which ensures the shortest path is found.
Cycle Detection: If, after the V-1 iterations, we can still relax an edge, it indicates a negative weight cycle.
3. What is MST (Minimum Spanning Tree)?
A Minimum Spanning Tree (MST) is a subgraph of a weighted graph that connects all the vertices with the minimum possible total edge weight. It must span all the vertices, and there must be no cycles in the tree.
MSTs have practical applications in network design (like road networks, electrical grids, etc.), as they minimize the cost of connecting all the nodes.
4. Prim’s Algorithm
Prim's Algorithm is one of the most popular algorithms to find the MST in a connected, undirected, and weighted graph. It works by building the spanning tree one vertex at a time, starting from an arbitrary vertex, and at each step, it adds the smallest edge that connects a vertex inside the tree to one outside the tree.
The key idea is to always pick the edge with the smallest weight and avoid forming a cycle. The time complexity of Prim's Algorithm using a priority queue is (O(E \log V)), where (E) is the number of edges and (V) is the number of vertices.
5. Prim’s Algorithm (Code)
Here's how Prim’s algorithm can be implemented using Java and a priority queue to efficiently select the smallest edge.
import java.util.*;
class PrimMST {
static class Edge {
int dest, weight;
Edge(int dest, int weight) {
this.dest = dest;
this.weight = weight;
}
}
static class Graph {
int V;
LinkedList<Edge>[] adjList;
Graph(int V) {
this.V = V;
adjList = new LinkedList[V];
for (int i = 0; i < V; i++) {
adjList[i] = new LinkedList<>();
}
}
void addEdge(int src, int dest, int weight) {
adjList[src].add(new Edge(dest, weight));
adjList[dest].add(new Edge(src, weight));
}
void primMST() {
boolean[] inMST = new boolean[V];
int[] key = new int[V];
int[] parent = new int[V];
Arrays.fill(key, Integer.MAX_VALUE);
key[0] = 0;
parent[0] = -1;
PriorityQueue<Integer> pq = new PriorityQueue<>(V, Comparator.comparingInt(i -> key[i]));
pq.add(0);
while (!pq.isEmpty()) {
int u = pq.poll();
inMST[u] = true;
for (Edge edge : adjList[u]) {
if (!inMST[edge.dest] && edge.weight < key[edge.dest]) {
key[edge.dest] = edge.weight;
pq.add(edge.dest);
parent[edge.dest] = u;
}
}
}
System.out.println("Edge \tWeight");
for (int i = 1; i < V; i++) {
System.out.println(parent[i] + " - " + i + "\t" + key[i]);
}
}
}
public static void main(String[] args) {
Graph g = new Graph(5);
g.addEdge(0, 1, 2);
g.addEdge(0, 3, 6);
g.addEdge(1, 2, 3);
g.addEdge(1, 3, 8);
g.addEdge(1, 4, 5);
g.addEdge(2, 4, 7);
g.addEdge(3, 4, 9);
g.primMST();
}
}
In this implementation:
Graph Representation: The graph is represented as an adjacency list, where each vertex holds a list of its edges.
Priority Queue: The priority queue helps efficiently pick the edge with the smallest weight at each step.
MST Construction: The algorithm grows the MST by repeatedly picking the smallest edge and adding it to the tree.
This chapter provides the essential tools needed to understand both shortest path algorithms and minimum spanning trees. Understanding these algorithms is crucial in solving network optimization problems, road network design, and various other practical applications.
See Also My Other Posts
To get a complete understanding of Java and Data Structures & Algorithms (DSA), check out these other chapters in my series:
1. Chapter 1: Variables and Data Types
This chapter is the foundation of Java programming. It covers the basics of variables, how to declare them, and the different data types available in Java (primitive and non-primitive). Understanding these concepts is crucial as they form the basis for everything you will do in Java. The post also explains memory allocation and how to choose the right data type for different scenarios.
2. Chapter 25: Queues
In this chapter, we delve into Queues, an essential data structure that follows the First-In-First-Out (FIFO) principle. The chapter explains how queues work, their applications in real-life scenarios (e.g., printer queue, customer service), and their implementation in Java. We also explore different types of queues, such as circular queues, priority queues, and double-ended queues (deques), with code examples to illustrate each.
3. Chapter 26: Greedy Algorithm
The Greedy Algorithm is a fundamental problem-solving technique in computer science. In this chapter, I explain the core concept of greedy algorithms—making the most optimal choice at each step—and how they differ from dynamic programming. We explore various problems, such as Activity Selection, Knapsack Problem, and Huffman Coding, showing how greedy algorithms provide efficient solutions. The post includes detailed code implementations and explanations to help you grasp the greedy approach effectively.
Visit My Other Series Also
Expand your knowledge in web development with my dedicated series:
1. Full Stack Java Development
This series walks through the entire journey of becoming a Full Stack Java Developer, from fundamentals like Java Programming and Object-Oriented Programming (OOP) to advanced topics like Spring Boot, REST APIs, and Database Integration. I cover front-end technologies like HTML, CSS, and JavaScript, and show how to integrate them with Java back-end systems.
2. Full Stack JavaScript Development
In this series, I focus on Full Stack JavaScript Development, covering the entire stack from front-end frameworks like React and Angular to back-end technologies such as Node.js, Express.js, and MongoDB. The series provides a comprehensive guide for building full-fledged web applications using JavaScript, the most popular language for web development today.
Also Visit My Profiles
Stay connected and explore my coding journey through my other profiles:
GitHub: Check out my open-source projects, coding exercises, and repositories for Java, JavaScript, and more.
LinkedIn: Connect with me professionally to stay updated on my learning path, projects, and posts.
LeetCode: Follow my progress as I solve coding challenges, optimize algorithms, and prepare for technical interviews.
Feel free to explore these resources for a more comprehensive learning experience!
Subscribe to my newsletter
Read articles from Rohit Gawande directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Rohit Gawande
Rohit Gawande
🚀 Tech Enthusiast | Full Stack Developer | System Design Explorer 💻 Passionate About Building Scalable Solutions and Sharing Knowledge Hi, I’m Rohit Gawande! 👋I am a Full Stack Java Developer with a deep interest in System Design, Data Structures & Algorithms, and building modern web applications. My goal is to empower developers with practical knowledge, best practices, and insights from real-world experiences. What I’m Currently Doing 🔹 Writing an in-depth System Design Series to help developers master complex design concepts.🔹 Sharing insights and projects from my journey in Full Stack Java Development, DSA in Java (Alpha Plus Course), and Full Stack Web Development.🔹 Exploring advanced Java concepts and modern web technologies. What You Can Expect Here ✨ Detailed technical blogs with examples, diagrams, and real-world use cases.✨ Practical guides on Java, System Design, and Full Stack Development.✨ Community-driven discussions to learn and grow together. Let’s Connect! 🌐 GitHub – Explore my projects and contributions.💼 LinkedIn – Connect for opportunities and collaborations.🏆 LeetCode – Check out my problem-solving journey. 💡 "Learning is a journey, not a destination. Let’s grow together!" Feel free to customize or add more based on your preferences! 😊