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