C Programming Data Structures Graph Representations Adjacency Matrix List Complete Guide

 Last Update:2025-06-23T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    8 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of C Programming data structures Graph Representations Adjacency Matrix List

Graph Representations in C Programming

Graphs are a fundamental data structure that represents relationships between entities. They are used in various applications, from social networking and mapping to complex network optimizations. In C programming, a graph can be represented in several ways. Two of the most common methods are using an adjacency matrix and an adjacency list. Below, I will explain these two representations in detail, highlighting the important aspects of each.

1. Adjacency Matrix Representation

An adjacency matrix is a 2D array where the rows and columns represent the vertices of the graph. The value stored in cell ( A[i][j] ) indicates the presence (and sometimes the weight) of an edge between vertex ( i ) and vertex ( j ). For an undirected graph, the matrix is symmetric.

Structure:

  • For an undirected graph with ( V ) vertices, the adjacency matrix ( A ) has dimensions ( V \times V ).
  • For a directed graph, the adjacency matrix can be asymmetric.

Operations:

  • Adding an edge: Set ( A[u][v] ) to 1 (or weight if weighted).
  • Checking if an edge exists: Check if ( A[u][v] ) is non-zero.
  • Removing an edge: Set ( A[u][v] ) to 0.

Advantages:

  • Fast and simple to implement.
  • Easy to determine the existence of an edge or to count the edges.
  • Efficient when the graph is dense, as it always uses ( V^2 ) space.

Disadvantages:

  • Consumes ( V^2 ) space even if the graph is sparse.
  • Adding and removing nodes requires restructuring the matrix.
  • Not suitable for large graphs due to high space complexity.

Implementation in C:

#include <stdio.h>
#include <stdbool.h>

#define VERTICES 5

int main() {
    int adjacencyMatrix[VERTICES][VERTICES] = {0};

    // Adding edges (u->v)
    adjacencyMatrix[0][1] = 1;
    adjacencyMatrix[1][0] = 1;  // Since it's undirected
    adjacencyMatrix[1][4] = 1;
    adjacencyMatrix[4][1] = 1;

    printf("Adjacency Matrix:\n");
    for (int i = 0; i < VERTICES; i++) {
        for (int j = 0; j < VERTICES; j++) {
            printf("%d ", adjacencyMatrix[i][j]);
        }
        printf("\n");
    }

    return 0;
}

Explanation of the code:

  • The matrix adjacencyMatrix is initialized with zeros.
  • Edges are added by setting the appropriate values.
  • The matrix is then printed, showing which vertices are connected.

2. Adjacency List Representation

An adjacency list represents a graph as a collection of linked lists or arrays. Each vertex has a list of adjacent vertices. It is often more space-efficient than an adjacency matrix, especially for sparse graphs.

Structure:

  • Each vertex has a list of neighboring vertices.
  • An adjacency list can be implemented using arrays or linked lists.

Operations:

  • Adding an edge: Append the vertex ( v ) to the list of vertex ( u ).
  • Checking if an edge exists: Traverse the list of vertex ( u ) to see if ( v ) is present.
  • Removing an edge: Remove ( v ) from the list of ( u ).

Advantages:

  • Space-efficient for sparse graphs.
  • Supports dynamic addition and removal of vertices and edges.
  • Allows easy traversal of vertices and edges.

Disadvantages:

  • Slower to determine if an edge exists between any two vertices (O(V) in the worst case).
  • More complex to implement.

Implementation in C using Array of Lists:

#include <stdio.h>
#include <stdlib.h>

#define VERTICES 5

struct Node {
    int vertex;
    struct Node* next;
};

struct Node* createNode(int v);
void addEdge(struct Node* adjacencyList[], int src, int dest);
void printGraph(struct Node* adjacencyList[], int vertices);

int main() {
    struct Node* adjacencyList[VERTICES];

    // Initialize adjacency list for each vertex
    for (int i = 0; i < VERTICES; i++) {
        adjacencyList[i] = NULL;
    }

    // Add edges
    addEdge(adjacencyList, 0, 1);
    addEdge(adjacencyList, 1, 4);
    addEdge(adjacencyList, 1, 2);
    addEdge(adjacencyList, 2, 3);

    printf("Adjacency List:\n");
    printGraph(adjacencyList, VERTICES);

    return 0;
}

// Create a new node
struct Node* createNode(int v) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->vertex = v;
    newNode->next = NULL;
    return newNode;
}

// Add an edge to the graph
void addEdge(struct Node* adjacencyList[], int src, int dest) {
    // Add an edge from source to destination
    struct Node* newNode = createNode(dest);
    newNode->next = adjacencyList[src];
    adjacencyList[src] = newNode;

    // Since undirected, add an edge from destination to source
    newNode = createNode(src);
    newNode->next = adjacencyList[dest];
    adjacencyList[dest] = newNode;
}

// Print the graph
void printGraph(struct Node* adjacencyList[], int vertices) {
    for (int v = 0; v < vertices; v++) {
        struct Node* node = adjacencyList[v];
        printf("Vertex %d: ", v);
        while (node != NULL) {
            printf("%d -> ", node->vertex);
            node = node->next;
        }
        printf("NULL\n");
    }
}

Explanation of the code:

  • An array of pointers adjacencyList is used, where each index represents a vertex and points to a linked list of adjacent vertices.
  • The function createNode creates a new node for a given vertex.
  • The function addEdge connects two vertices by adding nodes to each other’s adjacency lists.
  • The function printGraph traverses and prints each vertex and its adjacent vertices.

Summary

Both adjacency matrix and adjacency list representations are important in graph theory and have their own uses depending on the specific needs of the application. The adjacency matrix is simple and fast for dense graphs but consumes a lot of space. The adjacency list is more flexible and space-efficient for sparse graphs but requires more complex operations to manage.

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement C Programming data structures Graph Representations Adjacency Matrix List

Adjacency Matrix Representation

An adjacency matrix is a 2D array where the rows and columns represent vertices of the graph. The value at index (i, j) indicates whether an edge exists between vertex i and vertex j.

Step-by-Step Example

  1. Define the Graph Structure: An adjacency matrix for an undirected graph with V vertices will be a VxV matrix.

  2. Initialize the Graph: Create a function to initialize the graph by setting all values in the matrix to 0.

  3. Add an Edge: Create a function to add an edge between two vertices by setting the corresponding elements in the matrix to 1.

  4. Print the Adjacency Matrix: Create a function to print the adjacency matrix.

Complete Code

#include <stdio.h>
#include <stdlib.h>

#define V 5 // Number of vertices in the graph

// Graph structure
typedef struct {
    int adjMatrix[V][V];
} Graph;

// Initialize the graph
void initGraph(Graph *G) {
    for (int i = 0; i < V; i++) {
        for (int j = 0; j < V; j++) {
            G->adjMatrix[i][j] = 0;
        }
    }
}

// Add an edge to the graph
void addEdge(Graph *G, int v1, int v2) {
    if (v1 >= V || v2 >= V) {
        printf("Vertex out of range\n");
        return;
    }
    G->adjMatrix[v1][v2] = 1;
    G->adjMatrix[v2][v1] = 1; // Since it's undirected
}

// Print the adjacency matrix
void printGraph(Graph *G) {
    printf("Adjacency Matrix:\n");
    for (int i = 0; i < V; i++) {
        for (int j = 0; j < V; j++) {
            printf("%d ", G->adjMatrix[i][j]);
        }
        printf("\n");
    }
}

int main() {
    Graph G;
    initGraph(&G);

    addEdge(&G, 0, 1);
    addEdge(&G, 0, 2);
    addEdge(&G, 1, 2);
    addEdge(&G, 1, 3);
    addEdge(&G, 2, 4);
    
    printGraph(&G);

    return 0;
}

Output:

Adjacency Matrix:
0 1 1 0 0 
1 0 1 1 0 
1 1 0 0 1 
0 1 0 0 0 
0 0 1 0 0 

Adjacency List Representation

An adjacency list represents a graph as an array of lists. Each list describes the set of neighbors of a vertex i.

Step-by-Step Example

  1. Define the Node Structure: Define a structure to hold individual nodes of the adjacency list, which includes the vertex number and a pointer to the next node.

  2. Set Up the Graph Structure: Define a structure to represent the graph, which consists of an array of linked lists (one for each vertex).

  3. Initialize the Graph: Create a function to initialize the graph by setting the heads of all linked lists to NULL.

  4. Add an Edge: Create a function to add edges to the graph by adding nodes to the appropriate linked lists.

  5. Print the Graph: Create a function to print the graph by traversing each linked list and printing its values.

Complete Code

#include <stdio.h>
#include <stdlib.h>

#define V 5 // Number of vertices in the graph

// Node structure
typedef struct Node {
    int vertex;
    struct Node* next;
} Node;

// Graph structure
typedef struct Graph {
    Node* heads[V];
} Graph;

// Node creation
Node* createNode(int v) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->vertex = v;
    newNode->next = NULL;
    return newNode;
}

// Initialize the graph
void initGraph(Graph* G) {
    for (int i = 0; i < V; i++) {
        G->heads[i] = NULL;
    }
}

// Add an edge to the graph
void addEdge(Graph* G, int v1, int v2) {
    if (v1 >= V || v2 >= V) {
        printf("Vertex out of range\n");
        return;
    }
    Node* node = createNode(v2);
    node->next = G->heads[v1];
    G->heads[v1] = node;

    node = createNode(v1); // Since it's undirected
    node->next = G->heads[v2];
    G->heads[v2] = node;
}

// Print the graph
void printGraph(Graph* G) {
    for (int i = 0; i < V; i++) {
        printf("Vertex %d: ", i);
        Node* temp = G->heads[i];
        while (temp) {
            printf("%d -> ", temp->vertex);
            temp = temp->next;
        }
        printf("NULL\n");
    }
}

int main() {
    Graph G;
    initGraph(&G);

    addEdge(&G, 0, 1);
    addEdge(&G, 0, 2);
    addEdge(&G, 1, 2);
    addEdge(&G, 1, 3);
    addEdge(&G, 2, 4);
    
    printGraph(&G);

    return 0;
}

Output:

Vertex 0: 2 -> 1 -> NULL
Vertex 1: 3 -> 2 -> 0 -> NULL
Vertex 2: 4 -> 0 -> 1 -> NULL
Vertex 3: 1 -> NULL
Vertex 4: 2 -> NULL

Key Points

  • Adjacency Matrix: Best for dense graphs and fast lookups (O(1) for checking edges).
  • Adjacency List: Efficient in terms of memory and better for sparse graphs (adding and removing edges takes O(1)), but lookup takes O(V) in the worst case.

Both these implementations allow us to represent a graph flexibly, depending on the requirement of the project.

Top 10 Interview Questions & Answers on C Programming data structures Graph Representations Adjacency Matrix List

1. What is a Graph in C Programming?

Answer: A graph is a non-linear data structure consisting of a finite set of vertices (or nodes) and a set of edges that connect these vertices. In C programming, graphs can be represented using different structures like adjacency matrix and adjacency list.

2. How do you implement an adjacency matrix for a graph in C?

Answer: An adjacency matrix is a 2D array where the entry matrix[i][j] indicates whether there is an edge from vertex i to vertex j. Here's a simple implementation:

#include <stdio.h>
#define V 4 // Number of vertices

int main() {
    int adjMatrix[V][V];

    // Initialize matrix with zeros
    for(int i = 0; i < V; i++)
        for(int j = 0; j < V; j++)
            adjMatrix[i][j] = 0;

    // Adding edges (undirected graph for simplicity)
    adjMatrix[0][1] = 1;
    adjMatrix[1][0] = 1;
    adjMatrix[0][2] = 1;
    adjMatrix[2][0] = 1;
    adjMatrix[1][2] = 1;
    adjMatrix[2][1] = 1;
    adjMatrix[2][3] = 1;
    adjMatrix[3][2] = 1;

    // Displaying the adjacency matrix
    printf("Adjacency Matrix:\n");
    for(int i = 0; i < V; i++) {
        for(int j = 0; j < V; j++)
            printf("%d ", adjMatrix[i][j]);
        printf("\n");
    }

    return 0;
}

Each 1 represents an edge. Note that this example assumes an undirected graph.

3. What are the advantages of using an adjacency matrix?

Answer:

  • Simplicity: Easy to understand and implement.
  • Quick Edge Lookups: Determining if an edge exists between two vertices is O(1) through direct indexing.
  • Efficiency for Dense Graphs: When most pairs of vertices are connected, this is efficient due to constant space allocation based on V^2.

4. What are the disadvantages of using an adjacency matrix?

Answer:

  • Space Inefficiency: Requires O(V^2) space, which can be a lot for sparse graphs.
  • Edge Weight Storage Complexity: Storing weights for weighted graphs is straightforward but can lead to wasted space for sparse data.
  • Operations Like Add/Remove Vertices: These operations are inefficient as they require reallocation of the entire matrix.

5. How do you implement an adjacency list for a graph in C?

Answer: An adjacency list can be implemented using arrays of linked lists. Here’s an example:

#include <stdio.h>
#include <stdlib.h>

// Defining a node in the adjacency list
struct Node {
    int dest;
    struct Node* next;
};

// Function to create a new adjacency list node
struct Node* newAdjListNode(int dest) {
    struct Node* newNode = (struct Node*) malloc(sizeof(struct Node));
    newNode->dest = dest;
    newNode->next = NULL;
    return newNode;
}

// Function to create a graph with V vertices
struct Graph* createGraph(int V) {
    struct Graph* graph = (struct Graph*) malloc(sizeof(struct Graph));
    graph->V = V;

    // Create an array of adjacency lists. Size of the array will be V
    graph->array = (struct AdjList*) malloc(V * sizeof(struct AdjList));

    // Initialize each adjacency list as empty by making head as NULL
    for (int i = 0; i < V; ++i)
        graph->array[i].head = NULL;

    return graph;
}

// Example of how to add edges to this graph
void addEdge(struct Graph* graph, int src, int dest) {
    // Adding an edge from src to dest. A new node is added to the adjacency
    // list of src. The node is added at the beginning.
    struct Node* newNode = newAdjListNode(dest);
    newNode->next = graph->array[src].head;
    graph->array[src].head = newNode;

    // Since graph is undirected, add an edge from dest to src also.
    newNode = newAdjListNode(src);
    newNode->next = graph->array[dest].head;
    graph->array[dest].head = newNode;
}

int main() {
    int V = 5;
    struct Graph* graph = createGraph(V);
    addEdge(graph, 0, 1);
    addEdge(graph, 0, 4);
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 3);
    addEdge(graph, 1, 4);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 4);

    return 0;
}

6. What are the advantages of using an adjacency list?

Answer:

  • Space Efficiency: Only O(V+E) space is required, which is optimal for sparse graphs.
  • Dynamic Memory Usage: Easier to add or remove edges and vertices without reallocating all the storage.
  • Fast Edge Inserts/Removals: Operation speed is proportional to the degree of the vertex (O(degree)), much better than the matrix for sparse data.

7. What are the disadvantages of using an adjacency list?

Answer:

  • Slower Edge Queries: Checking for the existence of an edge between two nodes takes O(degree) time in the worst case.
  • More Complex Implementation: Requires handling pointers and potentially more code than a simple adjacency matrix.
  • Cache Performance Issues: Adjacency lists might have poorer cache performance compared to contiguous memory layouts in matrices.

8. When would you prefer using an adjacency matrix over an adjacency list?

Answer: Preferring an adjacency matrix over a list is suitable in the following scenarios:

  • Small graphs with dense connections: Where the ratio of edges to vertices is high, an adjacency matrix is more compact.
  • Frequent edge lookups: If your algorithm frequently checks the existence of edges, this structure offers faster queries.
  • Applications requiring fast updates of edge attributes: Changing an attribute in a matrix is O(1).

9. When would you prefer using an adjacency list over an adjacency matrix?

Answer: An adjacency list is preferable when:

  • Graphs have a large number of vertices and few edges: In such sparse graphs, it saves a significant amount of memory.
  • Insertions and deletions of edges are required: This is more efficiently handled by a list due to dynamic memory allocation.
  • Algorithm performance matters: For applications where memory usage and traversal efficiency are critical.

10. Provide a simple comparison between adjacency matrix and list implementations for storing a graph.

Answer:

| Feature | Adjacency Matrix | Adjacency List | |--------------------|---------------------------------------------------|-----------------------------------------------------------| | Space Usage | O(V^2), consumes more space even for sparse | O(V + E), efficient in terms of space | | Edge Insertion | O(1) | O(1) amortized, O(degree) worst-case | | Edge Removal | O(1) | Better for dense graphs, needs list traversal (O(degree)) | | Edge Query | O(1) | O(degree), requires scanning the list | | Ease Of Use | Simpler to implement | Slightly complex due to pointers | | Real-World Use | Ideal for dense graphs, easier if edge queries are | Optimal for sparse graphs, more efficient for operations | | | frequent | requiring modifications |

You May Like This Related .NET Topic

Login to post a comment.