Open In App

Kahn’s Algorithm vs DFS Approach: A Comparative Analysis

Topological sorting is a common problem in computer science that involves arranging the vertices of a directed acyclic graph (DAG) in a linear order such that for every directed edge (u, v), vertex u comes before vertex v in the ordering.

Two important methods to solve are:



Let us take the following example as input for both algorithms:

Directed Acyclic Graph

Kahn’s Algorithm:

Kahn’s Algorithm is a topological sorting algorithm that uses a queue-based approach to sort vertices in a DAG. It starts by finding vertices that have no incoming edges and adds them to a queue. It then removes a vertex from the queue and adds it to the sorted list. The algorithm continues this process, removing vertices with no incoming edges until all vertices have been sorted.



Below is the implementation of the Kahn’s approach:




// C++ Implementation
#include <bits/stdc++.h>
using namespace std;
class Solution {
 
public:
    // Topological sort using bfs is called
    // Kahn's Algorithm
    void topo_sort(vector<int> adj[], int n)
    {
        // Here concept of indegree used
        // as it represents number of incoming
        // edges on a node
        vector<int> indegree(n, 0);
        // Code for make indegree
        for (int i = 0; i < n; i++) {
            for (auto it : adj[i]) {
                indegree[it]++;
            }
        }
 
        vector<int> ans;
        queue<int> qrr;
        // Initially insert elements who has
        // indegree 0
        for (int i = 0; i < n; i++) {
 
            if (indegree[i] == 0) {
                qrr.push(i);
            }
        }
 
        while (!qrr.empty()) {
            // push those elements in queue which
            // poses 0 indegree
            int node = qrr.front();
 
            qrr.pop();
            ans.push_back(node);
            for (auto it : adj[node]) {
                indegree[it]--;
                if (indegree[it] == 0) {
                    qrr.push(it);
                }
            }
        }
        for (int i = 0; i < n; i++) {
            cout << ans[i] << " ";
        }
    }
};
 
// Function to add  edge
void addEdge(vector<int> adj[], int u, int v)
{
    adj[u].push_back(v);
}
 
// Driver code
int main()
{
    int n = 6;
    vector<int> adj[n];
    addEdge(adj, 5, 0);
    addEdge(adj, 5, 2);
    addEdge(adj, 2, 0);
    addEdge(adj, 2, 3);
    addEdge(adj, 3, 0);
    addEdge(adj, 3, 1);
    addEdge(adj, 1, 0);
    addEdge(adj, 4, 0);
    addEdge(adj, 4, 1);
    Solution s1;
 
    // Function call
    s1.topo_sort(adj, n);
    return 0;
}




import java.util.*;
 
class Solution {
 
    public void topo_sort(List<Integer>[] adj, int n)
    {
        List<Integer> indegree
            = new ArrayList<>(Collections.nCopies(n, 0));
 
        for (int i = 0; i < n; i++) {
            for (int it : adj[i]) {
                indegree.set(it, indegree.get(it) + 1);
            }
        }
 
        List<Integer> ans = new ArrayList<>();
        Queue<Integer> qrr = new LinkedList<>();
 
        for (int i = 0; i < n; i++) {
            if (indegree.get(i) == 0) {
                qrr.add(i);
            }
        }
 
        while (!qrr.isEmpty()) {
            int node = qrr.poll();
            ans.add(node);
 
            for (int it : adj[node]) {
                indegree.set(it, indegree.get(it) - 1);
 
                if (indegree.get(it) == 0) {
                    qrr.add(it);
                }
            }
        }
 
        for (int i = 0; i < n; i++) {
            System.out.print(ans.get(i) + " ");
        }
    }
}
 
class Main {
 
    @SuppressWarnings("unchecked")
    public static void main(String[] args)
    {
        int n = 6;
        List<Integer>[] adj = new ArrayList[n];
 
        for (int i = 0; i < n; i++) {
            adj[i] = new ArrayList<>();
        }
 
        addEdge(adj, 5, 0);
        addEdge(adj, 5, 2);
        addEdge(adj, 2, 0);
        addEdge(adj, 2, 3);
        addEdge(adj, 3, 0);
        addEdge(adj, 3, 1);
        addEdge(adj, 1, 0);
        addEdge(adj, 4, 0);
        addEdge(adj, 4, 1);
 
        Solution s1 = new Solution();
        s1.topo_sort(adj, n);
    }
 
    private static void addEdge(List<Integer>[] adj, int u,
                                int v)
    {
        adj[u].add(v);
    }
}




from typing import List, Deque
from collections import deque
 
class Solution:
    def topo_sort(self, adj: List[int], n: int) -> List[int]:
        # Create a list to store the in-degree of each node
        indegree = [0] * n
         
        # Calculate the in-degree of each node
        for i in range(n):
            for j in adj[i]:
                indegree[j] += 1
         
        # Create a queue to store nodes with in-degree 0
        q = deque()
        for i in range(n):
            if indegree[i] == 0:
                q.append(i)
         
        # Perform topological sorting using BFS
        ans = []
        while q:
            node = q.popleft()
            ans.append(node)
            for i in adj[node]:
                indegree[i] -= 1
                if indegree[i] == 0:
                    q.append(i)
         
        # Return the sorted nodes
        return ans
 
# Function to add an edge
def addEdge(adj: List[int], u: int, v: int) -> None:
    adj[u].append(v)
 
# Driver code
if __name__ == "__main__":
    n = 6
    adj = [[] for _ in range(n)]
    addEdge(adj, 5, 0)
    addEdge(adj, 5, 2)
    addEdge(adj, 2, 0)
    addEdge(adj, 2, 3)
    addEdge(adj, 3, 0)
    addEdge(adj, 3, 1)
    addEdge(adj, 1, 0)
    addEdge(adj, 4, 0)
    addEdge(adj, 4, 1)
    s = Solution()
 
    # Function call
    print(s.topo_sort(adj, n))




// C# Implementation
 
using System;
using System.Collections.Generic;
 
class Solution {
    // Topological sort using bfs is called
    // Kahn's Algorithm
    public void TopoSort(List<int>[] adj, int n)
    {
        // Here concept of indegree used
        // as it represents number of incoming
        // edges on a node
        int[] indegree = new int[n];
        // Code for make indegree
        for (int i = 0; i < n; i++) {
            foreach(int it in adj[i]) { indegree[it]++; }
        }
 
        List<int> ans = new List<int>();
        Queue<int> qrr = new Queue<int>();
 
        // Initially insert elements who has
        // indegree 0
        for (int i = 0; i < n; i++) {
            if (indegree[i] == 0) {
                qrr.Enqueue(i);
            }
        }
 
        while (qrr.Count > 0) {
            // push those elements in queue which
            // poses 0 indegree
            int node = qrr.Dequeue();
 
            ans.Add(node);
            foreach(int it in adj[node])
            {
                indegree[it]--;
                if (indegree[it] == 0) {
                    qrr.Enqueue(it);
                }
            }
        }
 
        for (int i = 0; i < n; i++) {
            Console.Write(ans[i] + " ");
        }
    }
}
 
// Driver code
public class GFG {
    static void Main(string[] args)
    {
        int n = 6;
        List<int>[] adj = new List<int>[ n ];
        for (int i = 0; i < n; i++) {
            adj[i] = new List<int>();
        }
        adj[5].Add(0);
        adj[5].Add(2);
        adj[2].Add(0);
        adj[2].Add(3);
        adj[3].Add(0);
        adj[3].Add(1);
        adj[1].Add(0);
        adj[4].Add(0);
        adj[4].Add(1);
 
        Solution s1 = new Solution();
 
        // Function call
        s1.TopoSort(adj, n);
    }
}




// Javadcript code addition
 
class Solution {
    topoSort(adj, n) {
        let indegree = new Array(n).fill(0);
        let ans = [];
        let qrr = [];
         
        // Code for make indegree
        for (let i = 0; i < n; i++) {
            for (let it of adj[i]) {
                indegree[it]++;
            }
        }
         
        // Initially insert elements who has indegree 0
        for (let i = 0; i < n; i++) {
            if (indegree[i] == 0) {
                qrr.push(i);
            }
        }
 
        while (qrr.length > 0) {
            let node = qrr.shift();
            ans.push(node);
            for (let it of adj[node]) {
                indegree[it]--;
                if (indegree[it] == 0) {
                    qrr.push(it);
                }
            }
        }
         
        for (let i = 0; i < n; i++) {
            process.stdout.write(ans[i] + " ");
        }
    }
}
 
// Function to add edge
function addEdge(adj, u, v) {
    adj[u].push(v);
}
 
// Driver code
let n = 6;
let adj = Array.from({ length: n }, () => []);
addEdge(adj, 5, 0);
addEdge(adj, 5, 2);
addEdge(adj, 2, 0);
addEdge(adj, 2, 3);
addEdge(adj, 3, 0);
addEdge(adj, 3, 1);
addEdge(adj, 1, 0);
addEdge(adj, 4, 0);
addEdge(adj, 4, 1);
let s1 = new Solution();
 
// Function call
s1.topoSort(adj, n);
 
// The code is contributed by Nidhi goel.

Output
4 5 2 3 1 0 





Time Complexity: O( V + E)
Auxiliary Space: O(V)

DFS Approach:

DFS Approach is a recursive algorithm that performs a depth-first search on the DAG. It starts at a vertex, explores as far as possible along each branch before backtracking, and marks visited vertices. During the DFS traversal, vertices are added to a stack in the order they are visited. Once the DFS traversal is complete, the stack is reversed to obtain the topological ordering.

Below is the implementation of the DFS approach:




#include <bits/stdc++.h>
using namespace std;
class Solution {
 
    // Topo sort only exists in DAGs i.e.
    // Direct Acyclic graph
    void dfs(vector<int> adj[], vector<int>& vis, int node,
             int n, stack<int>& stck)
    {
        vis[node] = 1;
        for (auto it : adj[node]) {
            if (!vis[it]) {
                dfs(adj, vis, it, n, stck);
            }
        }
        stck.push(node);
    }
 
public:
    // During the traversal u must
    // be visited before v
    stack<int> topo_sort(vector<int> adj[], int n)
    {
        vector<int> vis(n, 0);
 
        // using stack ADT
        stack<int> stck;
        for (int i = 0; i < n; i++) {
            if (!vis[i]) {
                dfs(adj, vis, i, n, stck);
            }
        }
        return stck;
    }
};
 
void addEdge(vector<int> adj[], int u, int v)
{
    adj[u].push_back(v);
}
 
// Drivers code
int main()
{
    int n = 6;
    vector<int> adj[n];
    addEdge(adj, 5, 0);
    addEdge(adj, 5, 2);
    addEdge(adj, 2, 0);
    addEdge(adj, 2, 3);
    addEdge(adj, 3, 0);
    addEdge(adj, 3, 1);
    addEdge(adj, 1, 0);
    addEdge(adj, 4, 0);
    addEdge(adj, 4, 1);
    Solution s1;
    stack<int> ans = s1.topo_sort(adj, n);
    for (int i = 0; i < n; i++) {
        int n = ans.top();
        ans.pop();
        cout << n << " ";
    }
    return 0;
}




// Java Implementation
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
 
class Solution {
    // Topo sort only exists in DAGs i.e.
    // Direct Acyclic Graph
    void dfs(List<Integer>[] adj, List<Integer> vis, int node, int n, Stack<Integer> stck) {
        vis.set(node, 1);
        for (int it : adj[node]) {
            if (vis.get(it) == 0) {
                dfs(adj, vis, it, n, stck);
            }
        }
        stck.push(node);
    }
 
    // During the traversal u must
    // be visited before v
    Stack<Integer> topo_sort(List<Integer>[] adj, int n) {
        List<Integer> vis = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            vis.add(0);
        }
         
        //  using stack ADT
        Stack<Integer> stck = new Stack<>();
        for (int i = 0; i < n; i++) {
            if (vis.get(i) == 0) {
                dfs(adj, vis, i, n, stck);
            }
        }
        return stck;
    }
}
 
public class Main {
    static void addEdge(List<Integer>[] adj, int u, int v) {
        adj[u].add(v);
    }
 
    public static void main(String[] args) {
        int n = 6;
        List<Integer>[] adj = new ArrayList[n];
        for (int i = 0; i < n; i++) {
            adj[i] = new ArrayList<>();
        }
 
        addEdge(adj, 5, 0);
        addEdge(adj, 5, 2);
        addEdge(adj, 2, 0);
        addEdge(adj, 2, 3);
        addEdge(adj, 3, 0);
        addEdge(adj, 3, 1);
        addEdge(adj, 1, 0);
        addEdge(adj, 4, 0);
        addEdge(adj, 4, 1);
 
        Solution solution = new Solution();
        Stack<Integer> ans = solution.topo_sort(adj, n);
        while (!ans.isEmpty()) {
            int node = ans.pop();
            System.out.print(node + " ");
        }
    }
}
 
// This code is contributed by Utkarsh Kumar




# Python implementation of the above approach
class Solution:
    # Topo sort only exists in DAGs i.e.
    # Direct Acyclic graph
    def dfs(self, adj, vis, node, n, stck):
        vis[node] = 1
        for it in adj[node]:
            if not vis[it]:
                self.dfs(adj, vis, it, n, stck)
        stck.append(node)
 
    # During the traversal u must
    # be visited before v
    def topo_sort(self, adj, n):
        vis = [0] * n
 
        # using stack ADT
        stck = []
        for i in range(n):
            if not vis[i]:
                self.dfs(adj, vis, i, n, stck)
        return stck
 
 
def addEdge(adj, u, v):
    adj[u].append(v)
 
 
# Drivers code
n = 6
adj = [[] for _ in range(n)]
addEdge(adj, 5, 0)
addEdge(adj, 5, 2)
addEdge(adj, 2, 0)
addEdge(adj, 2, 3)
addEdge(adj, 3, 0)
addEdge(adj, 3, 1)
addEdge(adj, 1, 0)
addEdge(adj, 4, 0)
addEdge(adj, 4, 1)
s1 = Solution()
ans = s1.topo_sort(adj, n)
for i in range(n):
    num = ans.pop()
    print(num, end=" ")
# This code is contributed by Tapesh(tapeshdua420)




using System;
using System.Collections.Generic;
 
class Solution
{
    // Topo sort only exists in DAGs i.e.
    // Direct Acyclic Graph
    void DFS(List<int>[] adj, List<int> vis, int node, int n, Stack<int> stck)
    {
        vis[node] = 1;
        foreach (int it in adj[node])
        {
            if (vis[it] == 0)
            {
                DFS(adj, vis, it, n, stck);
            }
        }
        stck.Push(node);
    }
 
    // During the traversal, u must
    // be visited before v
    public Stack<int> TopoSort(List<int>[] adj, int n)
    {
        List<int> vis = new List<int>(n);
        for (int i = 0; i < n; i++)
        {
            vis.Add(0);
        }
 
        // using stack ADT
        Stack<int> stck = new Stack<int>();
        for (int i = 0; i < n; i++)
        {
            if (vis[i] == 0)
            {
                DFS(adj, vis, i, n, stck);
            }
        }
        return stck;
    }
}
 
class Program
{
    static void AddEdge(List<int>[] adj, int u, int v)
    {
        adj[u].Add(v);
    }
 
    static void Main(string[] args)
    {
        int n = 6;
        List<int>[] adj = new List<int>[n];
        for (int i = 0; i < n; i++)
        {
            adj[i] = new List<int>();
        }
 
        AddEdge(adj, 5, 0);
        AddEdge(adj, 5, 2);
        AddEdge(adj, 2, 0);
        AddEdge(adj, 2, 3);
        AddEdge(adj, 3, 0);
        AddEdge(adj, 3, 1);
        AddEdge(adj, 1, 0);
        AddEdge(adj, 4, 0);
        AddEdge(adj, 4, 1);
 
        Solution solution = new Solution();
        Stack<int> ans = solution.TopoSort(adj, n);
 
        while (ans.Count > 0)
        {
            int node = ans.Pop();
            Console.Write(node + " ");
        }
    }
}




class Solution {
    // Topo sort only exists in DAGs i.e.
    // Direct Acyclic Graph
    dfs(adj, vis, node, n, stck) {
        vis[node] = 1;
        for (let it of adj[node]) {
            if (!vis[it]) {
                this.dfs(adj, vis, it, n, stck);
            }
        }
        stck.push(node);
    }
 
    // During the traversal, u must
    // be visited before v
    topo_sort(adj, n) {
        const vis = new Array(n).fill(0);
 
        // Using stack data structure
        const stck = [];
        for (let i = 0; i < n; i++) {
            if (!vis[i]) {
                this.dfs(adj, vis, i, n, stck);
            }
        }
        return stck.reverse();
    }
}
 
function addEdge(adj, u, v) {
    adj[u].push(v);
}
 
const n = 6;
const adj = Array.from({ length: n }, () => []);
 
addEdge(adj, 5, 0);
addEdge(adj, 5, 2);
addEdge(adj, 2, 0);
addEdge(adj, 2, 3);
addEdge(adj, 3, 0);
addEdge(adj, 3, 1);
addEdge(adj, 1, 0);
addEdge(adj, 4, 0);
addEdge(adj, 4, 1);
 
const solution = new Solution();
const ans = solution.topo_sort(adj, n);
 
console.log(ans.join(" "));

Output
5 4 2 3 1 0 





Time Complexity: O( V + E)
Auxiliary Space: O(V)

Comparative Analysis :

Advantages of Kahn’s Algorithm over the DFS Approach:

Disadvantages of Kahn’s Algorithm compared to the DFS Approach:

Advantages of the DFS Approach over Kahn’s Algorithm:

Disadvantages of the DFS Approach compared to Kahn’s Algorithm:

Conclusion:

In conclusion, both Kahn’s Algorithm and DFS Approach are effective algorithms for topological sorting in DAGs. The choice between the two depends on the size of the graph, the available memory, and the required performance. In general, Kahn’s Algorithm is simpler and more reliable, but DFS Approach can be more efficient for larger graphs. Both algorithms have their strengths and weaknesses, and the best approach depends on the specific requirements of the problem at hand.


Article Tags :