Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
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.
