Open In App

C Program for Topological Sorting

Last Updated : 25 Sep, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

A fundamental procedure in computer science called topological sorting is used to arrange the nodes in a directed network. This sorting method makes sure that vertex u is placed before vertex v in the sorted order for each directed edge (u, v). Numerous fields, including work scheduling, project planning, and dependency resolution, use topological sorting extensively.

Prerequisites: Loops, Structures, Graphs.

Topological Sorting

Topological Sorting is the process in which the main goal is to find an ordering of vertices in a directed acyclic graph (DAG) that places vertex u before vertex v for any directed edge (u, v). “Topological order” is the name given to this linear arrangement. If a DAG contains cycles, it might not have any topological ordering at all.

For example, a topological sorting of the following graph is “5 4 2 3 1 0”. There can be more than one topological sorting for a graph. Another topological sorting of the following graph is “4 5 2 3 1 0”. The first vertex in topological sorting is always a vertex with an in-degree of 0 (a vertex with no incoming edges).

topological sorting in c

DAG Graph

C Program for Topological Sorting

C




// C Program to implement Topological Sorting
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
  
// Structure to represent a stack
struct Stack {
    int data;
    struct Stack* next;
};
  
struct Graph {
    int V; // No. of vertices
    // Pointer to an array containing adjacency lists
    struct List* adj;
};
  
// Structure to represent a list (adjacency list)
struct List {
    int data;
    struct List* next;
};
  
// Create a new node for the stack
struct Stack* createStackNode(int data)
{
    struct Stack* newNode
        = (struct Stack*)malloc(sizeof(struct Stack));
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}
  
// Create a new node for the adjacency list
struct List* createListNode(int data)
{
    struct List* newNode
        = (struct List*)malloc(sizeof(struct List));
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}
  
// Function to initialize a graph with V vertices
struct Graph* createGraph(int V)
{
    struct Graph* graph
        = (struct Graph*)malloc(sizeof(struct Graph));
    graph->V = V;
    graph->adj
        = (struct List*)malloc(V * sizeof(struct List));
    for (int i = 0; i < V; ++i) {
        graph->adj[i].next = NULL;
    }
    return graph;
}
  
// Function to add an edge to the graph
void addEdge(struct Graph* graph, int v, int w)
{
    struct List* newNode = createListNode(w);
    newNode->next = graph->adj[v].next;
    graph->adj[v].next = newNode;
}
  
// A recursive function used by topologicalSort
void topologicalSortUtil(struct Graph* graph, int v,
                         bool visited[],
                         struct Stack** stack)
{
    visited[v] = true;
  
    struct List* current = graph->adj[v].next;
    while (current != NULL) {
        int adjacentVertex = current->data;
        if (!visited[adjacentVertex]) {
            topologicalSortUtil(graph, adjacentVertex,
                                visited, stack);
        }
        current = current->next;
    }
  
    // Push the current vertex to stack which stores the
    // result
    struct Stack* newNode = createStackNode(v);
    newNode->next = *stack;
    *stack = newNode;
}
  
// The function to do Topological Sort. It uses recursive
// topologicalSortUtil
void topologicalSort(struct Graph* graph)
{
    struct Stack* stack = NULL;
  
    // Mark all the vertices as not visited
    bool* visited = (bool*)malloc(graph->V * sizeof(bool));
    for (int i = 0; i < graph->V; ++i) {
        visited[i] = false;
    }
  
    // Call the recursive helper function to store
    // Topological Sort starting from all vertices one by
    // one
    for (int i = 0; i < graph->V; ++i) {
        if (!visited[i]) {
            topologicalSortUtil(graph, i, visited, &stack);
        }
    }
  
    // Print contents of stack
    while (stack != NULL) {
        printf("%d ", stack->data);
        struct Stack* temp = stack;
        stack = stack->next;
        free(temp);
    }
  
    // Free allocated memory
    free(visited);
    free(graph->adj);
    free(graph);
}
  
// Driver program to test above functions
int main()
{
    // Create a graph given in the above diagram
    struct Graph* g = createGraph(6);
    addEdge(g, 5, 2);
    addEdge(g, 5, 0);
    addEdge(g, 4, 0);
    addEdge(g, 4, 1);
    addEdge(g, 2, 3);
    addEdge(g, 3, 1);
  
    printf("Topological Sorting Order: ");
    topologicalSort(g);
  
    return 0;
}


Output

Topological Sorting Order: 5 4 2 3 1 0 
  • Time Complexity: O(V+E)., where V is the number of vertices and E is the number of edges. The above algorithm is simply DFS with an extra stack. So time complexity is the same as DFS.
  • Auxiliary space: O(V). The extra space is needed for the stack

Applications of Topological Sorting

Topological sorting has numerous real-world applications, including:

  • In project management, it helps determine the order in which tasks should be executed to minimize dependencies and improve project efficiency.
  • Compilers use topological sorting to resolve dependencies between functions and optimize code generation.
  • Package managers like APT and YUM use topological sorting to manage software dependencies efficiently.
  • Universities use topological sorting to establish course prerequisites, ensuring that students take courses in the correct order.


Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads