Find the ordering of tasks from given dependencies in C++

Finding the ordering of tasks from given dependencies is a classic topological sorting problem. Given n tasks labeled from 0 to n-1 with prerequisite relationships, we need to determine a valid execution order. This problem uses Depth-First Search (DFS) with cycle detection to ensure all dependencies are satisfied.

Problem Understanding

If we have a prerequisite pair [2, 1], it means task 1 must be completed before task 2. We need to find an order where all prerequisites are satisfied, or return an empty array if it's impossible due to circular dependencies.

Algorithm Approach

The solution uses DFS-based topological sorting with three key components ?

  • Graph Construction ? Build an adjacency list representing task dependencies

  • Cycle Detection ? Use onpath array to detect circular dependencies

  • Topological Order ? Use post-order traversal to generate valid task sequence

Implementation

#include <bits/stdc++.h>
using namespace std;

vector<unordered_set<int>> create_graph(int n, vector<pair<int, int>>& pre) {
    vector<unordered_set<int>> graph(n);
    for (auto prerequisite : pre)
        graph[prerequisite.second].insert(prerequisite.first);
    return graph;
}

bool dfs(vector<unordered_set<int>>& graph, int start, vector<bool>& onpath, 
         vector<bool>& visited, vector<int>& toposort) {
    if (visited[start])
        return false;
    
    onpath[start] = visited[start] = true;
    
    for (int neighbor : graph[start])
        if (onpath[neighbor] || dfs(graph, neighbor, onpath, visited, toposort))
            return true;
    
    toposort.push_back(start);
    return onpath[start] = false;
}

vector<int> get_order(int n, vector<pair<int, int>> &pre) {
    vector<unordered_set<int>> graph = create_graph(n, pre);
    vector<int> toposort;
    vector<bool> onpath(n, false), visited(n, false);
    
    for (int i = 0; i < n; i++)
        if (!visited[i] && dfs(graph, i, onpath, visited, toposort))
            return {};
    
    reverse(toposort.begin(), toposort.end());
    return toposort;
}

int main() {
    int n = 5;
    vector<pair<int, int>> pre = {{1, 0}, {2, 0}, {3, 2}, {3, 1}, {4, 0}};
    vector<int> result = get_order(n, pre);
    
    cout << "Task execution order: ";
    for (int i = 0; i < result.size(); i++) {
        cout << result[i];
        if (i < result.size() - 1) cout << " ";
    }
    cout << endl;
    
    return 0;
}

The output of the above code is ?

Task execution order: 0 1 4 2 3

How It Works

The algorithm works in three phases ?

Phase Purpose Key Component
Graph Construction Build dependency relationships Adjacency list using unordered_set
DFS Traversal Detect cycles and build order visited and onpath arrays
Result Generation Reverse post-order for topological sort Reverse the toposort vector

Key Variables

  • visited[] ? Tracks completely processed nodes

  • onpath[] ? Detects back edges (cycles) during current DFS path

  • toposort[] ? Stores nodes in post-order (reversed for final result)

Complexity Analysis

Time Complexity: O(V + E) where V is the number of tasks and E is the number of prerequisite relationships.

Space Complexity: O(V + E) for the graph representation and auxiliary arrays.

Conclusion

This DFS-based topological sorting efficiently solves task scheduling with dependencies. It detects impossible scenarios through cycle detection and returns a valid execution order when possible.

Updated on: 2026-03-25T09:54:03+05:30

503 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements