Skip to content

Commit bceeb16

Browse files
Add TopologicalSortDFS with cycle detection
1 parent 8e1f124 commit bceeb16

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.ArrayDeque;
4+
import java.util.ArrayList;
5+
import java.util.Deque;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
/**
11+
* Topological Sort using Depth-First Search (DFS).
12+
*
13+
* <p>
14+
* A topological ordering of a directed acyclic graph (DAG) is a linear ordering
15+
* of its vertices such that for every directed edge u → v, vertex u appears
16+
* before vertex v in the ordering.
17+
*
18+
* <p>
19+
* This implementation uses DFS with a 3-state visited array:
20+
* <ul>
21+
* <li>UNVISITED – node has not been visited yet</li>
22+
* <li>IN_PROGRESS – node is on the current DFS path (used for cycle
23+
* detection)</li>
24+
* <li>DONE – node and all its descendants are fully processed</li>
25+
* </ul>
26+
*
27+
* <p>
28+
* Time Complexity: O(V + E), where V = vertices, E = edges
29+
* Space Complexity: O(V + E) for the adjacency list and recursion stack
30+
*
31+
* @see <a href="https://en.wikipedia.org/wiki/Topological_sorting">Topological
32+
* Sorting (Wikipedia)</a>
33+
*/
34+
public final class TopologicalSortDFS {
35+
36+
private TopologicalSortDFS() {
37+
}
38+
39+
private enum VisitState {
40+
UNVISITED,
41+
IN_PROGRESS,
42+
DONE
43+
}
44+
45+
private final Map<Integer, List<Integer>> adjacencyList = new HashMap<>();
46+
47+
/**
48+
* Adds a directed edge from vertex {@code u} to vertex {@code v}.
49+
* Both vertices are added to the graph if not already present.
50+
*
51+
* @param u the source vertex
52+
* @param v the destination vertex
53+
*/
54+
public void addEdge(int u, int v) {
55+
adjacencyList.computeIfAbsent(u, k -> new ArrayList<>()).add(v);
56+
adjacencyList.computeIfAbsent(v, k -> new ArrayList<>());
57+
}
58+
59+
/**
60+
* Adds an isolated vertex (no edges) to the graph.
61+
*
62+
* @param v the vertex to add
63+
*/
64+
public void addVertex(int v) {
65+
adjacencyList.computeIfAbsent(v, k -> new ArrayList<>());
66+
}
67+
68+
/**
69+
* Performs a topological sort of the graph using DFS.
70+
*
71+
* @return a {@link List} of vertices in topologically sorted order
72+
* @throws IllegalStateException if the graph contains a cycle (i.e., is not a
73+
* DAG)
74+
*/
75+
public List<Integer> topologicalSort() {
76+
Map<Integer, VisitState> visitState = new HashMap<>();
77+
for (int vertex : adjacencyList.keySet()) {
78+
visitState.put(vertex, VisitState.UNVISITED);
79+
}
80+
81+
Deque<Integer> stack = new ArrayDeque<>();
82+
83+
for (int vertex : adjacencyList.keySet()) {
84+
if (visitState.get(vertex) == VisitState.UNVISITED) {
85+
dfs(vertex, visitState, stack);
86+
}
87+
}
88+
89+
List<Integer> result = new ArrayList<>();
90+
while (!stack.isEmpty()) {
91+
result.add(stack.pop());
92+
}
93+
return result;
94+
}
95+
96+
/**
97+
* Recursive DFS helper that pushes fully processed nodes onto the stack.
98+
*
99+
* @param vertex the current vertex being visited
100+
* @param visitState map tracking the visit state of each vertex
101+
* @param stack stack accumulating vertices in reverse finish order
102+
* @throws IllegalStateException if a back edge (cycle) is detected
103+
*/
104+
private void dfs(int vertex, Map<Integer, VisitState> visitState, Deque<Integer> stack) {
105+
visitState.put(vertex, VisitState.IN_PROGRESS);
106+
107+
for (int neighbor : adjacencyList.get(vertex)) {
108+
VisitState state = visitState.get(neighbor);
109+
if (state == VisitState.IN_PROGRESS) {
110+
// Back edge found → cycle exists → not a DAG
111+
throw new IllegalStateException(
112+
"Graph contains a cycle. Topological sort is not possible.");
113+
}
114+
if (state == VisitState.UNVISITED) {
115+
dfs(neighbor, visitState, stack);
116+
}
117+
}
118+
119+
// All descendants processed — mark done and push to stack
120+
visitState.put(vertex, VisitState.DONE);
121+
stack.push(vertex);
122+
}
123+
}

0 commit comments

Comments
 (0)