|
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 | + * @namespace topological_sort |
| 30 | + * @brief Topological Sort Algorithm |
| 31 | + */ |
| 32 | +namespace topological_sort { |
| 33 | +/** |
| 34 | + * @class Graph |
| 35 | + * @brief Class that represents a directed graph and provides methods for |
| 36 | + * manipulating the graph |
| 37 | + */ |
| 38 | +class Graph { |
| 39 | + private: |
| 40 | + int n; // Number of nodes |
| 41 | + std::vector<std::vector<int>> adj; // Adjacency list representation |
| 42 | + |
| 43 | + public: |
| 44 | + /** |
| 45 | + * @brief Constructor for the Graph class |
| 46 | + * @param nodes Number of nodes in the graph |
| 47 | + */ |
| 48 | + Graph(int nodes) : n(nodes), adj(nodes) {} |
| 49 | + |
| 50 | + /** |
| 51 | + * @brief Function that adds an edge between two nodes or vertices of graph |
| 52 | + * @param u Start node of the edge |
| 53 | + * @param v End node of the edge |
| 54 | + */ |
| 55 | + void addEdge(int u, int v) { adj[u].push_back(v); } |
| 56 | + |
| 57 | + /** |
| 58 | + * @brief Get the adjacency list of the graph |
| 59 | + * @returns A reference to the adjacency list |
| 60 | + */ |
| 61 | + const std::vector<std::vector<int>>& getAdjacencyList() const { |
| 62 | + return adj; |
| 63 | + } |
| 64 | + |
| 65 | + /** |
| 66 | + * @brief Get the number of nodes in the graph |
| 67 | + * @returns The number of nodes |
| 68 | + */ |
| 69 | + int getNumNodes() const { return n; } |
| 70 | +}; |
| 71 | + |
| 72 | +/** |
| 73 | + * @brief Function to perform Depth First Search on the graph |
| 74 | + * @param v Starting vertex for depth-first search |
| 75 | + * @param visited Array representing whether each node has been visited |
| 76 | + * @param graph Adjacency list of the graph |
| 77 | + * @param s Stack containing the vertices for topological sorting |
| 78 | + */ |
| 79 | +void dfs(int v, std::vector<int>& visited, |
| 80 | + const std::vector<std::vector<int>>& graph, std::stack<int>& s) { |
| 81 | + visited[v] = 1; |
| 82 | + for (int neighbour : graph[v]) { |
| 83 | + if (!visited[neighbour]) { |
| 84 | + dfs(neighbour, visited, graph, s); |
16 | 85 | }
|
17 | 86 | }
|
18 |
| - topological_order.push_back(v); |
| 87 | + s.push(v); |
19 | 88 | }
|
20 | 89 |
|
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) { |
| 90 | +/** |
| 91 | + * @brief Function to get the topological sort of the graph |
| 92 | + * @param g Graph object |
| 93 | + * @returns A vector containing the topological order of nodes |
| 94 | + */ |
| 95 | +std::vector<int> topologicalSort(const Graph& g) { |
| 96 | + int n = g.getNumNodes(); |
| 97 | + const auto& adj = g.getAdjacencyList(); |
| 98 | + std::vector<int> visited(n, 0); |
| 99 | + std::stack<int> s; |
| 100 | + |
| 101 | + for (int i = 0; i < n; i++) { |
25 | 102 | if (!visited[i]) {
|
26 |
| - dfs(i); |
| 103 | + dfs(i, visited, adj, s); |
27 | 104 | }
|
28 | 105 | }
|
29 |
| - reverse(topological_order.begin(), topological_order.end()); |
| 106 | + |
| 107 | + std::vector<int> ans; |
| 108 | + while (!s.empty()) { |
| 109 | + int elem = s.top(); |
| 110 | + s.pop(); |
| 111 | + ans.push_back(elem); |
| 112 | + } |
| 113 | + |
| 114 | + if (ans.size() < n) { // Cycle detected |
| 115 | + throw std::invalid_argument("cycle detected in graph"); |
| 116 | + } |
| 117 | + return ans; |
30 | 118 | }
|
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); |
41 |
| - } |
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. |
| 119 | +} // namespace topological_sort |
| 120 | +} // namespace graph |
| 121 | + |
| 122 | +/** |
| 123 | + * @brief Self-test implementation |
| 124 | + * @returns void |
| 125 | + */ |
| 126 | +static void test() { |
| 127 | + // Test 1 |
| 128 | + std::cout << "Testing for graph 1\n"; |
| 129 | + int n_1 = 6; |
| 130 | + graph::topological_sort::Graph graph1(n_1); |
| 131 | + graph1.addEdge(4, 0); |
| 132 | + graph1.addEdge(5, 0); |
| 133 | + graph1.addEdge(5, 2); |
| 134 | + graph1.addEdge(2, 3); |
| 135 | + graph1.addEdge(3, 1); |
| 136 | + graph1.addEdge(4, 1); |
| 137 | + std::vector<int> ans_1 = graph::topological_sort::topologicalSort(graph1); |
| 138 | + std::vector<int> expected_1 = {5, 4, 2, 3, 1, 0}; |
| 139 | + std::cout << "Topological Sorting Order: "; |
| 140 | + for (int i : ans_1) { |
| 141 | + std::cout << i << " "; |
| 142 | + } |
| 143 | + std::cout << '\n'; |
| 144 | + assert(ans_1 == expected_1); |
| 145 | + std::cout << "Test Passed\n\n"; |
| 146 | + |
| 147 | + // Test 2 |
| 148 | + std::cout << "Testing for graph 2\n"; |
| 149 | + int n_2 = 5; |
| 150 | + graph::topological_sort::Graph graph2(n_2); |
| 151 | + graph2.addEdge(0, 1); |
| 152 | + graph2.addEdge(0, 2); |
| 153 | + graph2.addEdge(1, 2); |
| 154 | + graph2.addEdge(2, 3); |
| 155 | + graph2.addEdge(1, 3); |
| 156 | + graph2.addEdge(2, 4); |
| 157 | + std::vector<int> ans_2 = graph::topological_sort::topologicalSort(graph2); |
| 158 | + std::vector<int> expected_2 = {0, 1, 2, 4, 3}; |
| 159 | + std::cout << "Topological Sorting Order: "; |
| 160 | + for (int i : ans_2) { |
| 161 | + std::cout << i << " "; |
47 | 162 | }
|
48 | 163 | std::cout << '\n';
|
| 164 | + assert(ans_2 == expected_2); |
| 165 | + std::cout << "Test Passed\n\n"; |
| 166 | + |
| 167 | + // Test 3 - Graph with cycle |
| 168 | + std::cout << "Testing for graph 3\n"; |
| 169 | + int n_3 = 3; |
| 170 | + graph::topological_sort::Graph graph3(n_3); |
| 171 | + graph3.addEdge(0, 1); |
| 172 | + graph3.addEdge(1, 2); |
| 173 | + graph3.addEdge(2, 0); |
| 174 | + try { |
| 175 | + graph::topological_sort::topologicalSort(graph3); |
| 176 | + } catch (std::invalid_argument& err) { |
| 177 | + assert(std::string(err.what()) == "cycle detected in graph"); |
| 178 | + } |
| 179 | + std::cout << "Test Passed\n"; |
| 180 | +} |
| 181 | + |
| 182 | +/** |
| 183 | + * @brief Main function |
| 184 | + * @returns 0 on exit |
| 185 | + */ |
| 186 | +int main() { |
| 187 | + test(); // run self test implementations |
49 | 188 | return 0;
|
50 | 189 | }
|
0 commit comments