|
1 |
| -#include <algorithm> |
2 |
| -#include <iostream> |
3 |
| -#include <vector> |
4 |
| - |
5 |
| -int number_of_vertices, |
6 |
| - number_of_edges; // For number of Vertices (V) and number of edges (E) |
7 |
| -std::vector<std::vector<int>> graph; |
8 |
| -std::vector<bool> visited; |
9 |
| -std::vector<int> topological_order; |
10 |
| - |
11 |
| -void dfs(int v) { |
12 |
| - visited[v] = true; |
13 |
| - for (int u : graph[v]) { |
14 |
| - if (!visited[u]) { |
15 |
| - dfs(u); |
| 1 | +/** |
| 2 | + * @file |
| 3 | + * @brief [Topological Sort |
| 4 | + * Algorithm](https://en.wikipedia.org/wiki/Topological_sorting) |
| 5 | + * @details |
| 6 | + * Topological sorting of a directed graph is a linear ordering or its vertices |
| 7 | + * such that for every directed edge (u,v) from vertex u to vertex v, u comes |
| 8 | + * before v in the oredering. |
| 9 | + * |
| 10 | + * A topological sort is possible only in a directed acyclic graph (DAG). |
| 11 | + * This file contains code of finding topological sort using Kahn's Algorithm |
| 12 | + * which involves using Depth First Search technique |
| 13 | + */ |
| 14 | + |
| 15 | +#include <algorithm> // For std::reverse |
| 16 | +#include <cassert> // For assert |
| 17 | +#include <iostream> // For IO operations |
| 18 | +#include <stack> // For std::stack |
| 19 | +#include <stdexcept> // For std::invalid_argument |
| 20 | +#include <vector> // For std::vector |
| 21 | + |
| 22 | +/** |
| 23 | + * @namespace graph |
| 24 | + * @brief Graph algorithms |
| 25 | + */ |
| 26 | +namespace graph { |
| 27 | + |
| 28 | +/** |
| 29 | + * @brief Function that add edge between two nodes or vertices of graph |
| 30 | + * @param u any node or vertex of graph |
| 31 | + * @param v any node or vertex of graph |
| 32 | + * @returns None |
| 33 | + */ |
| 34 | +void addEdge(std::vector<std::vector<int>>& adj, int u, int v) { |
| 35 | + adj[u].push_back(v); |
| 36 | +} |
| 37 | + |
| 38 | +/** |
| 39 | + * @brief Function to perform Depth First Search on the graph |
| 40 | + * @param v starting vertex for depth first search |
| 41 | + * @param vistied array representing if a node is visited |
| 42 | + * @param graph adjecancy list of the graph |
| 43 | + * @param s stack containing the vertex of topological sort |
| 44 | + * @returns None |
| 45 | + */ |
| 46 | +void dfs(int v, std::vector<int>& visited, std::vector<std::vector<int>>& graph, |
| 47 | + std::stack<int>& s) { |
| 48 | + visited[v] = 1; |
| 49 | + for (int i = 0; i < graph[v].size(); i++) { |
| 50 | + int neighbour = graph[v][i]; |
| 51 | + if (!visited[neighbour]) { |
| 52 | + dfs(neighbour, visited, graph, s); |
16 | 53 | }
|
17 | 54 | }
|
18 |
| - topological_order.push_back(v); |
| 55 | + s.push(v); |
19 | 56 | }
|
20 | 57 |
|
21 |
| -void topological_sort() { |
22 |
| - visited.assign(number_of_vertices, false); |
23 |
| - topological_order.clear(); |
24 |
| - for (int i = 0; i < number_of_vertices; ++i) { |
| 58 | +/** |
| 59 | + * @brief Function to get the topological sort of the graph |
| 60 | + * @param graph adjecancy list of the graph |
| 61 | + * @param n number of nodes in the graph |
| 62 | + */ |
| 63 | +std::vector<int> topological_sort(std::vector<std::vector<int>>& graph, int n) { |
| 64 | + std::vector<int> visited(n, 0); |
| 65 | + std::stack<int> s; |
| 66 | + for (int i = 0; i < n; i++) { |
25 | 67 | if (!visited[i]) {
|
26 |
| - dfs(i); |
| 68 | + dfs(i, visited, graph, s); |
27 | 69 | }
|
28 | 70 | }
|
29 |
| - reverse(topological_order.begin(), topological_order.end()); |
| 71 | + std::vector<int> ans; |
| 72 | + while (!s.empty()) { |
| 73 | + int elem = s.top(); |
| 74 | + s.pop(); |
| 75 | + ans.push_back(elem); |
| 76 | + } |
| 77 | + if (ans.size() < n) { // Cycle detected |
| 78 | + throw std::invalid_argument("cycle present in graph"); |
| 79 | + } |
| 80 | + return ans; |
30 | 81 | }
|
31 |
| -int main() { |
32 |
| - std::cout |
33 |
| - << "Enter the number of vertices and the number of directed edges\n"; |
34 |
| - std::cin >> number_of_vertices >> number_of_edges; |
35 |
| - int x = 0, y = 0; |
36 |
| - graph.resize(number_of_vertices, std::vector<int>()); |
37 |
| - for (int i = 0; i < number_of_edges; ++i) { |
38 |
| - std::cin >> x >> y; |
39 |
| - x--, y--; // to convert 1-indexed to 0-indexed |
40 |
| - graph[x].push_back(y); |
| 82 | +} // namespace graph |
| 83 | + |
| 84 | +/** |
| 85 | + * @brief Self-test implementation |
| 86 | + * @returns void |
| 87 | + */ |
| 88 | +static void test() { |
| 89 | + // Test 1; |
| 90 | + std::cout << "Testing for graph 1\n"; |
| 91 | + int n_1 = 6; |
| 92 | + std::vector<std::vector<int>> adj_1(n_1); |
| 93 | + graph::addEdge(adj_1, 4, 0); |
| 94 | + graph::addEdge(adj_1, 5, 0); |
| 95 | + graph::addEdge(adj_1, 5, 2); |
| 96 | + graph::addEdge(adj_1, 2, 3); |
| 97 | + graph::addEdge(adj_1, 3, 1); |
| 98 | + graph::addEdge(adj_1, 4, 1); |
| 99 | + std::vector<int> ans_1 = graph::topological_sort(adj_1, n_1); |
| 100 | + std::vector<int> expected_1 = {5, 4, 2, 3, 1, 0}; |
| 101 | + std::cout << "Topological Sorting Order: "; |
| 102 | + for (int i : ans_1) { |
| 103 | + std::cout << i << " "; |
41 | 104 | }
|
42 |
| - topological_sort(); |
43 |
| - std::cout << "Topological Order : \n"; |
44 |
| - for (int v : topological_order) { |
45 |
| - std::cout << v + 1 |
46 |
| - << ' '; // converting zero based indexing back to one based. |
| 105 | + std::cout << '\n'; |
| 106 | + assert(ans_1 == expected_1); |
| 107 | + std::cout << "Test Passed\n\n"; |
| 108 | + |
| 109 | + // Test 2 |
| 110 | + std::cout << "Testing for graph 2\n"; |
| 111 | + int n_2 = 5; |
| 112 | + std::vector<std::vector<int>> adj_2(n_2); |
| 113 | + graph::addEdge(adj_2, 0, 1); |
| 114 | + graph::addEdge(adj_2, 0, 2); |
| 115 | + graph::addEdge(adj_2, 1, 2); |
| 116 | + graph::addEdge(adj_2, 2, 3); |
| 117 | + graph::addEdge(adj_2, 1, 3); |
| 118 | + graph::addEdge(adj_2, 2, 4); |
| 119 | + std::vector<int> ans_2 = graph::topological_sort(adj_2, n_2); |
| 120 | + std::vector<int> expected_2 = {0, 1, 2, 4, 3}; |
| 121 | + std::cout << "Topological Sorting Order: "; |
| 122 | + for (int i : ans_2) { |
| 123 | + std::cout << i << " "; |
47 | 124 | }
|
48 | 125 | std::cout << '\n';
|
| 126 | + assert(ans_2 == expected_2); |
| 127 | + std::cout << "Test Passed\n\n"; |
| 128 | + |
| 129 | + // Test 3 - Graph with cycle |
| 130 | + std::cout << "Testing for graph 3\n"; |
| 131 | + int n_3 = 3; |
| 132 | + std::vector<std::vector<int>> adj_3(n_3); |
| 133 | + graph::addEdge(adj_3, 0, 1); |
| 134 | + graph::addEdge(adj_3, 1, 2); |
| 135 | + graph::addEdge(adj_3, 2, 0); |
| 136 | + try { |
| 137 | + graph::topological_sort(adj_3, n_3); |
| 138 | + } catch (std::invalid_argument& err) { |
| 139 | + assert(std::string(err.what()) == "cycle detected in graph"); |
| 140 | + } |
| 141 | + std::cout << "Test Passed \n"; |
| 142 | +} |
| 143 | + |
| 144 | +/** |
| 145 | + * @brief Main function |
| 146 | + * @returns 0 on exit |
| 147 | + */ |
| 148 | +int main() { |
| 149 | + test(); |
49 | 150 | return 0;
|
50 | 151 | }
|
0 commit comments