Introduction to Graph Data Structure with Practical Examples

Have you ever tried to find the quickest way to get somewhere on a map when the streets are busy? And have you noticed how easy it is to see who your friends know on social media? Well, both of these things use something called a graph. It’s like a map that helps computers figure out connections and the best routes. It’s what makes finding friends and navigating streets so smooth.

¶Graph Data Structure

A graph is a collection of nodes (vertices) interconnected by edges . This abstraction allows us to represent various relationships between objects or entities. Formally, a graph G is defined as a pair (V, E) , where V represents the set of vertices or nodes, and E represents the set of edges connecting these nodes.
In computer science and mathematics, the graph data structure stands as a fundamental concept with far-reaching applications. From social networks to transportation systems, algorithms leveraging graphs power a wide range of modern technologies.

Graph Data Structure

¶Components of a Graph Data Structure

A graph is comprised of multiple components that work together to define its structure and properties. These components include,

¶Types of Graphs

Graphs can be categorized in various ways depending on the types of nodes and edges, as well as their relationships.

Understanding the different types of graphs and their characteristics is essential for effectively modeling real-world scenarios and selecting appropriate algorithms for graph analysis and manipulation.

¶Representation of a Graph in Graph Data Structure

Graphs can be represented in programming using various data structures, each offering unique advantages depending on the specific requirements of the application. Here are some common representations:

¶Adjacency Matrix:

An adjacency matrix is a 2D array where each cell represents the presence or absence of an edge between two vertices. For an unweighted graph, the cell value can be either 0 or 1 to denote absence or presence of an edge respectively. For a weighted graph, the cell value can represent the weight of the edge. This representation is efficient for dense graphs but can be memory-intensive for sparse graphs.

#include #define MAX_VERTICES 5 int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // Function to add an edge between vertices u and v with weight w void addEdge(int u, int v, int w) < adjMatrix[u][v] = w; adjMatrix[v][u] = w; // For undirected graph, add this line >int main() < // Initialize adjacency matrix with all zeros for (int i = 0; i < MAX_VERTICES; i++) < for (int j = 0; j < MAX_VERTICES; j++) < adjMatrix[i][j] = 0; >> // Add edges addEdge(0, 1, 5); addEdge(0, 2, 3); addEdge(1, 2, 2); addEdge(2, 4, 5); // Add more edges as needed // Print adjacency matrix printf("Adjacency Matrix:\n"); for (int i = 0; i < MAX_VERTICES; i++) < for (int j = 0; j < MAX_VERTICES; j++) < printf("%d ", adjMatrix[i][j]); >printf("\n"); > return 0; > 
Output:
Adjacency Matrix:
0 5 3 0 0
5 0 2 0 0
3 2 0 0 5
0 0 0 0 0
0 0 5 0 0

¶Adjacency List:

An adjacency list is a collection of lists or arrays where each list corresponds to a vertex in the graph. Each list contains the vertices adjacent to the corresponding vertex. This representation is more memory-efficient for sparse graphs compared to adjacency matrices. It allows for fast traversal of neighbors of a vertex.

#include #include #define MAX_VERTICES 5 typedef struct Node < int vertex; int weight; struct Node* next; >Node; Node* adjacencyList[MAX_VERTICES]; // Function to add an edge between vertices u and v with weight w void addEdge(int u, int v, int w) < Node* newNode = (Node*)malloc(sizeof(Node)); newNode->vertex = v; newNode->weight = w; newNode->next = adjacencyList[u]; adjacencyList[u] = newNode; // For undirected graph, add this line newNode = (Node*)malloc(sizeof(Node)); newNode->vertex = u; newNode->weight = w; newNode->next = adjacencyList[v]; adjacencyList[v] = newNode; > int main() < // Initialize adjacency list with NULL for (int i = 0; i < MAX_VERTICES; i++) < adjacencyList[i] = NULL; >// Add edges addEdge(0, 1, 5); addEdge(0, 2, 3); addEdge(1, 2, 2); addEdge(2, 4, 5); // Add more edges as needed // Print adjacency list printf("Adjacency List:\n"); for (int i = 0; i < MAX_VERTICES; i++) < printf("Vertex %d: ", i); Node* current = adjacencyList[i]; while (current != NULL) < printf("(%d, %d) ", current->vertex, current->weight); current = current->next; > printf("\n"); > return 0; > 

Output:
Adjacency List:
Vertex 0: (2, 3) (1, 5)
Vertex 1: (2, 2) (0, 5)
Vertex 2: (4, 5) (1, 2) (0, 3)
Vertex 3:
Vertex 4: (2, 5)

¶Edge List:

An edge list is a list of tuples or objects, where each tuple/object represents an edge in the graph. Each tuple/object contains information about the vertices that the edge connects and, optionally, the weight of the edge. This representation is simple and compact, making it suitable for small graphs.

#include #define MAX_EDGES 5 typedef struct Edge < int src, dest, weight; >Edge; Edge edgeList[MAX_EDGES]; int numEdges = 0; // Function to add an edge between vertices u and v with weight w void addEdge(int u, int v, int w) < edgeList[numEdges].src = u; edgeList[numEdges].dest = v; edgeList[numEdges].weight = w; numEdges++; >int main() < // Add edges addEdge(0, 1, 5); addEdge(0, 2, 3); addEdge(1, 2, 2); addEdge(2, 4, 5); // Print edge list printf("Edge List:\n"); for (int i = 0; i < numEdges; i++) < printf("Edge %d: (%d, %d) Weight: %d\n", i, edgeList[i].src, edgeList[i].dest, edgeList[i].weight); >return 0; > 

Output:
Edge List:
Edge 0: (0, 1) Weight: 5
Edge 1: (0, 2) Weight: 3
Edge 2: (1, 2) Weight: 2
Edge 3: (2, 4) Weight: 5

¶Notable Graph Algorithms

Graph algorithms are essential tools in computer science and are widely used in various applications. Here are some useful graph algorithms and their common applications:

¶Depth-First Search (DFS):

¶Breadth-First Search (BFS):

¶Dijkstra’s Algorithm:

¶Bellman-Ford Algorithm:

¶Prim’s Algorithm & Kruskal’s Algorithm:

¶A* Search Algorithm:

¶Min-Cost Flow Algorithm:

¶Applications of Graph Data Structure

The graph data structure finds application in various domains. Here are some notable applications:

These applications highlight the versatility and importance of graph data structures in solving complex problems across various domains, making them a fundamental concept in computer science and beyond.

¶Run C Programming Online Compiler

To make your learning more effective, exercise the coding examples in the text editor below.