11/**
2- * This topological sort implementation takes an adjacency list of an acyclic graph and returns an
3- * array with the indexes of the nodes in a (non unique) topological order which tells you how to
4- * process the nodes in the graph. More precisely from wiki: A topological ordering is a linear
5- * ordering of its vertices such that for every directed edge uv from vertex u to vertex v, u comes
6- * before v in the ordering.
2+ * Topological sort implementation using DFS on an adjacency list of a Directed Acyclic Graph (DAG).
3+ * Returns an array with the node indexes in a (non-unique) topological order: for every directed
4+ * edge u -> v, u comes before v in the ordering.
75 *
8- * <p>Time Complexity: O(V + E)
6+ * <p>Also includes a method to find shortest paths in a DAG using the topological ordering.
7+ *
8+ * <p>Time: O(V + E)
9+ *
10+ * <p>Space: O(V + E)
911 *
1012 * @author William Fiset, william.alexandre.fiset@gmail.com
1113 */
1517
1618public class TopologicalSortAdjacencyList {
1719
18- // Helper Edge class to describe edges in the graph
1920 static class Edge {
2021 int from , to , weight ;
2122
22- public Edge (int f , int t , int w ) {
23- from = f ;
24- to = t ;
25- weight = w ;
23+ public Edge (int from , int to , int weight ) {
24+ this . from = from ;
25+ this . to = to ;
26+ this . weight = weight ;
2627 }
2728 }
2829
29- // Helper method that performs a depth first search on the graph to give
30- // us the topological ordering we want. Instead of maintaining a stack
31- // of the nodes we see we simply place them inside the ordering array
32- // in reverse order for simplicity.
33- private static int dfs (
34- int i , int at , boolean [] visited , int [] ordering , Map <Integer , List <Edge >> graph ) {
35-
36- visited [at ] = true ;
37-
38- List <Edge > edges = graph .get (at );
39-
40- if (edges != null )
41- for (Edge edge : edges ) if (!visited [edge .to ]) i = dfs (i , edge .to , visited , ordering , graph );
42-
43- ordering [i ] = at ;
44- return i - 1 ;
45- }
46-
47- // Finds a topological ordering of the nodes in a Directed Acyclic Graph (DAG)
48- // The input to this function is an adjacency list for a graph and the number
49- // of nodes in the graph.
50- //
51- // NOTE: 'numNodes' is not necessarily the number of nodes currently present
52- // in the adjacency list since you can have singleton nodes with no edges which
53- // wouldn't be present in the adjacency list but are still part of the graph!
54- //
30+ /**
31+ * Finds a topological ordering of the nodes in a DAG.
32+ *
33+ * <p>NOTE: {@code numNodes} is not necessarily the number of nodes in the adjacency list since
34+ * singleton nodes with no edges wouldn't be present but are still part of the graph.
35+ *
36+ * @param graph adjacency list mapping node index to outgoing edges.
37+ * @param numNodes total number of nodes in the graph.
38+ * @return array of node indexes in topological order.
39+ */
5540 public static int [] topologicalSort (Map <Integer , List <Edge >> graph , int numNodes ) {
56-
5741 int [] ordering = new int [numNodes ];
5842 boolean [] visited = new boolean [numNodes ];
5943
6044 int i = numNodes - 1 ;
6145 for (int at = 0 ; at < numNodes ; at ++)
62- if (!visited [at ]) i = dfs (i , at , visited , ordering , graph );
46+ if (!visited [at ])
47+ i = dfs (i , at , visited , ordering , graph );
6348
6449 return ordering ;
6550 }
6651
67- // A useful application of the topological sort is to find the shortest path
68- // between two nodes in a Directed Acyclic Graph (DAG). Given an adjacency list
69- // this method finds the shortest path to all nodes starting at 'start'
70- //
71- // NOTE: 'numNodes' is not necessarily the number of nodes currently present
72- // in the adjacency list since you can have singleton nodes with no edges which
73- // wouldn't be present in the adjacency list but are still part of the graph!
74- //
75- public static Integer [] dagShortestPath (Map <Integer , List <Edge >> graph , int start , int numNodes ) {
52+ // Places nodes into the ordering array in reverse DFS post-order.
53+ private static int dfs (
54+ int i , int at , boolean [] visited , int [] ordering , Map <Integer , List <Edge >> graph ) {
55+ visited [at ] = true ;
56+
57+ List <Edge > edges = graph .get (at );
58+ if (edges != null )
59+ for (Edge edge : edges )
60+ if (!visited [edge .to ])
61+ i = dfs (i , edge .to , visited , ordering , graph );
62+
63+ ordering [i ] = at ;
64+ return i - 1 ;
65+ }
7666
67+ /**
68+ * Finds the shortest path from {@code start} to all other reachable nodes in a DAG.
69+ *
70+ * <p>NOTE: {@code numNodes} is not necessarily the number of nodes in the adjacency list since
71+ * singleton nodes with no edges wouldn't be present but are still part of the graph.
72+ *
73+ * @param graph adjacency list mapping node index to outgoing edges.
74+ * @param start the source node.
75+ * @param numNodes total number of nodes in the graph.
76+ * @return array of shortest distances, null for unreachable nodes.
77+ */
78+ public static Integer [] dagShortestPath (
79+ Map <Integer , List <Edge >> graph , int start , int numNodes ) {
7780 int [] topsort = topologicalSort (graph , numNodes );
7881 Integer [] dist = new Integer [numNodes ];
7982 dist [start ] = 0 ;
8083
8184 for (int i = 0 ; i < numNodes ; i ++) {
82-
8385 int nodeIndex = topsort [i ];
84- if (dist [nodeIndex ] != null ) {
85- List <Edge > adjacentEdges = graph .get (nodeIndex );
86- if (adjacentEdges != null ) {
87- for (Edge edge : adjacentEdges ) {
88-
89- int newDist = dist [nodeIndex ] + edge .weight ;
90- if (dist [edge .to ] == null ) dist [edge .to ] = newDist ;
91- else dist [edge .to ] = Math .min (dist [edge .to ], newDist );
92- }
93- }
86+ if (dist [nodeIndex ] == null )
87+ continue ;
88+ for (Edge edge : graph .getOrDefault (nodeIndex , Collections .emptyList ())) {
89+ int newDist = dist [nodeIndex ] + edge .weight ;
90+ if (dist [edge .to ] == null || newDist < dist [edge .to ])
91+ dist [edge .to ] = newDist ;
9492 }
9593 }
9694
9795 return dist ;
9896 }
9997
100- // Example usage of topological sort
10198 public static void main (String [] args ) {
102-
103- // Graph setup
10499 final int N = 7 ;
105100 Map <Integer , List <Edge >> graph = new HashMap <>();
106- for (int i = 0 ; i < N ; i ++) graph .put (i , new ArrayList <>());
101+ for (int i = 0 ; i < N ; i ++)
102+ graph .put (i , new ArrayList <>());
103+
107104 graph .get (0 ).add (new Edge (0 , 1 , 3 ));
108105 graph .get (0 ).add (new Edge (0 , 2 , 2 ));
109106 graph .get (0 ).add (new Edge (0 , 5 , 3 ));
@@ -115,18 +112,10 @@ public static void main(String[] args) {
115112 graph .get (5 ).add (new Edge (5 , 4 , 7 ));
116113
117114 int [] ordering = topologicalSort (graph , N );
115+ System .out .println (Arrays .toString (ordering ));
118116
119- // // Prints: [6, 0, 5, 1, 2, 3, 4]
120- System .out .println (java .util .Arrays .toString (ordering ));
121-
122- // Finds all the shortest paths starting at node 0
123117 Integer [] dists = dagShortestPath (graph , 0 , N );
124-
125- // Find the shortest path from 0 to 4 which is 8.0
126- System .out .println (dists [4 ]);
127-
128- // Find the shortest path from 0 to 6 which
129- // is null since 6 is not reachable!
130- System .out .println (dists [6 ]);
118+ System .out .println (dists [4 ]); // 8
119+ System .out .println (dists [6 ]); // null (unreachable)
131120 }
132121}
0 commit comments