This section introduces Graphs, a versatile data structure used to model relationships between objects. Graphs are fundamental in solving problems across domains like computer networks, social networks, navigation systems, and more.
A Graph is a data structure consisting of:
- Vertices (Nodes): Represent the objects.
- Edges: Represent the relationships between vertices.
Graphs can be directed (edges have direction) or undirected (edges are bidirectional). They may also be weighted, where edges have associated costs or weights.
-
Directed Graph:
- Edges have a specific direction.
- Example: One-way roads in navigation.
-
Undirected Graph:
- Edges are bidirectional.
- Example: Two-way roads or social connections.
-
Weighted Graph:
- Edges have weights representing costs, distances, or capacities.
- Example: Transportation networks with travel times or costs.
-
Unweighted Graph:
- Edges have no weights.
- Example: Friendship networks.
-
Cyclic Graph:
- Contains at least one cycle (path that starts and ends at the same vertex).
-
Acyclic Graph:
- Does not contain any cycles.
- A 2D array where
matrix[i][j]represents the edge between vertexiand vertexj. - Suitable for dense graphs.
- A list where each vertex has a list of adjacent vertices.
- Efficient for sparse graphs.
-
Graph Traversal:
- Depth First Search (DFS): Explores as far as possible along each branch before backtracking.
- Breadth First Search (BFS): Explores all neighbors of a vertex before moving to the next level.
-
Shortest Path:
- Dijkstra's Algorithm: Finds the shortest path in weighted graphs.
- Bellman-Ford Algorithm: Handles graphs with negative weights.
- Floyd-Warshall Algorithm: Computes shortest paths between all pairs of vertices.
-
Minimum Spanning Tree:
- Prim's Algorithm: Builds the tree incrementally by adding the smallest edge.
- Kruskal's Algorithm: Adds edges in increasing weight order to avoid cycles.
-
Cycle Detection:
- Detects cycles in directed or undirected graphs.
-
Topological Sorting:
- Orders vertices in a directed acyclic graph (DAG) based on dependencies.
- Navigation Systems: Finding the shortest paths in maps.
- Social Networks: Modeling and analyzing relationships.
- Computer Networks: Routing and data flow optimization.
- Dependency Resolution: Task scheduling and dependency graphs.
Node Code:
public class Node {
private final char data; // Data stored in the node
// Constructor to initialize the node with data
public Node(char data) {
this.data = data;
}
// Getter to access the data
public char getData() {
return data;
}
}Graph Code:
public class Graph {
private final ArrayList<Node> nodes; // List of nodes (vertices) in the graph
private final int[][] matrix; // Adjacency matrix to represent edges
// Constructor to initialize the graph with a specified size
public Graph(int size) {
nodes = new ArrayList<>(size);
matrix = new int[size][size];
}
// Add a node (vertex) to the graph
public void addNode(Node node) {
if (nodes.size() < matrix.length) {
nodes.add(node);
} else {
System.out.println("Cannot add more nodes. Graph is at full capacity.");
}
}
// Add a directed edge between two nodes (src -> dst)
public void addEdge(int src, int dst) {
matrix[src][dst] = 1;
}
// Check if an edge exists between two nodes
public boolean checkEdge(int src, int dst) {
return matrix[src][dst] == 1;
}
// Print the adjacency matrix along with node labels
public void print() {
// Print the header row with node labels
System.out.print(" ");
for (Node node : nodes) {
System.out.print(node.getData() + " ");
}
System.out.println();
// Print each row with node labels and corresponding edges
for (int i = 0; i < nodes.size(); i++) {
System.out.print(nodes.get(i).getData() + " ");
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
}
}Main Code:
import java.util.ArrayList;
public class AdjacencyMatrix {
public static void main(String[] args) {
// Initialize a graph with 5 nodes
Graph graph = new Graph(5);
// Adding nodes (vertices) to the graph
graph.addNode(new Node('A')); // Node 0
graph.addNode(new Node('B')); // Node 1
graph.addNode(new Node('C')); // Node 2
graph.addNode(new Node('D')); // Node 3
graph.addNode(new Node('E')); // Node 4
// Adding edges between the nodes
graph.addEdge(0, 1); // Edge: A -> B
graph.addEdge(1, 2); // Edge: B -> C
graph.addEdge(2, 3); // Edge: C -> D
graph.addEdge(2, 4); // Edge: C -> E
graph.addEdge(4, 0); // Edge: E -> A
graph.addEdge(4, 2); // Edge: E -> C
// Printing the adjacency matrix
graph.print();
// Checking if edge exists
System.out.println();
System.out.println("Checking if edge exist between 'C' and 'E': " + graph.checkEdge(2, 4));
System.out.println("Checking if edge exist between 'A' and 'E': " + graph.checkEdge(0, 4));
}
}Output:
A B C D E
A 0 1 0 0 0
B 0 0 1 0 0
C 0 0 0 1 1
D 0 0 0 0 0
E 1 0 1 0 0
Checking if edge exist between 'C' and 'E': true
Checking if edge exist between 'A' and 'E': false
Node Code:
public class Node {
private final char data; // Data stored in the node
// Constructor to initialize the node with data
public Node(char data) {
this.data = data;
}
// Getter to access the data
public char getData() {
return data;
}
}Graph Code:
import java.util.ArrayList;
import java.util.LinkedList;
public class Graph {
private final ArrayList<LinkedList<Node>> adjacencyList; // List of adjacency lists to represent edges
// Constructor to initialize the graph
public Graph() {
adjacencyList = new ArrayList<>();
}
// Add a node (vertex) to the graph
public void addNode(Node node) {
LinkedList<Node> currentList = new LinkedList<>();
currentList.add(node); // Each adjacency list starts with the node itself
adjacencyList.add(currentList);
}
// Add a directed edge between two nodes (src -> dst)
public void addEdge(int src, int dst) {
if (src < adjacencyList.size() && dst < adjacencyList.size()) {
LinkedList<Node> srcList = adjacencyList.get(src);
Node dstNode = adjacencyList.get(dst).getFirst(); // Get the destination node
srcList.add(dstNode); // Add the destination node to the source's adjacency list
} else {
System.out.println("Invalid node indices. Edge not added.");
}
}
// Check if an edge exists between two nodes
public boolean checkEdge(int src, int dst) {
if (src < adjacencyList.size() && dst < adjacencyList.size()) {
LinkedList<Node> srcList = adjacencyList.get(src);
Node dstNode = adjacencyList.get(dst).getFirst();
return srcList.contains(dstNode); // Check if the destination node exists in the source's adjacency list
}
return false;
}
// Print the adjacency list
public void print() {
for (LinkedList<Node> nodes : adjacencyList) {
System.out.print(nodes.getFirst().getData() + " -> ");
for (int j = 1; j < nodes.size(); j++) { // Skip the first node (itself)
System.out.print(nodes.get(j).getData() + " ");
}
System.out.println();
}
}
}Main Code:
public class AdjacencyList {
public static void main(String[] args) {
// Initialize a graph
Graph graph = new Graph();
// Adding nodes (vertices) to the graph
graph.addNode(new Node('A')); // Node 0
graph.addNode(new Node('B')); // Node 1
graph.addNode(new Node('C')); // Node 2
graph.addNode(new Node('D')); // Node 3
graph.addNode(new Node('E')); // Node 4
// Adding edges between the nodes
graph.addEdge(0, 1); // Edge: A -> B
graph.addEdge(1, 2); // Edge: B -> C
graph.addEdge(1, 4); // Edge: B -> E
graph.addEdge(2, 3); // Edge: C -> D
graph.addEdge(2, 4); // Edge: C -> E
graph.addEdge(4, 0); // Edge: E -> A
graph.addEdge(4, 2); // Edge: E -> C
// Printing the adjacency list
graph.print();
// Checking if edges exist
System.out.println();
System.out.println("Checking if edge exists between 'A' and 'D': " + graph.checkEdge(0, 3)); // false
System.out.println("Checking if edge exists between 'B' and 'E': " + graph.checkEdge(1, 4)); // true
}
}Output:
A -> B
B -> C E
C -> D E
D ->
E -> A C
Checking if edge exists between 'A' and 'D': false
Checking if edge exists between 'B' and 'E': true