diff --git a/README.md b/README.md
index 95482aec3..22a934dd3 100644
--- a/README.md
+++ b/README.md
@@ -216,14 +216,12 @@ $ java -cp classes com.williamfiset.algorithms.search.BinarySearch
- [:movie_camera:](https://www.youtube.com/watch?v=4NQ3HnhyNfQ) [Floyd Warshall algorithm (adjacency matrix, negative cycle check)](src/main/java/com/williamfiset/algorithms/graphtheory/FloydWarshallSolver.java) **- O(V3)**
- [:movie_camera:](https://www.youtube.com/watch?v=cIBFEhD77b4) [Kahn's algorithm (topological sort, adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/Kahns.java) **- O(E+V)**
- [Kruskal's min spanning tree algorithm (edge list, union find)](src/main/java/com/williamfiset/algorithms/graphtheory/KruskalsEdgeList.java) **- O(Elog(E))**
-- [:movie_camera:](https://www.youtube.com/watch?v=JZBQLXgSGfs) [Kruskal's min spanning tree algorithm (edge list, union find, lazy sorting)](src/main/java/com/williamfiset/algorithms/graphtheory/KruskalsEdgeListPartialSortSolver.java) **- O(Elog(E))**
- [Kosaraju's strongly connected components algorithm (adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/Kosaraju.java) **- O(V+E)**
- [:movie_camera:](https://www.youtube.com/watch?v=jsmMtJpPnhU) [Prim's min spanning tree algorithm (lazy version, adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/LazyPrimsAdjacencyList.java) **- O(Elog(E))**
- [:movie_camera:](https://www.youtube.com/watch?v=xq3ABa-px_g) [Prim's min spanning tree algorithm (eager version, adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/EagerPrimsAdjacencyList.java) **- O(Elog(V))**
- [Steiner tree (minimum spanning tree generalization)](src/main/java/com/williamfiset/algorithms/graphtheory/SteinerTree.java) **- O(V3 + V2 _ 2T + V _ 3T)**
- [:movie_camera:](https://www.youtube.com/watch?v=wUgWX0nc4NY) [Tarjan's strongly connected components algorithm (adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/TarjanSccSolverAdjacencyList.java) **- O(V+E)**
- [:movie_camera:](https://www.youtube.com/watch?v=eL-KzMXSXXI) [Topological sort (acyclic graph, adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/TopologicalSortAdjacencyList.java) **- O(V+E)**
-- [Traveling Salesman Problem (brute force)](src/main/java/com/williamfiset/algorithms/graphtheory/TspBruteForce.java) **- O(n!)**
- [:movie_camera:](https://www.youtube.com/watch?v=cY4HiiFHO1o) [Traveling Salesman Problem (dynamic programming, iterative)](src/main/java/com/williamfiset/algorithms/graphtheory/TspDynamicProgrammingIterative.java) **- O(n22n)**
- [Traveling Salesman Problem (dynamic programming, recursive)](src/main/java/com/williamfiset/algorithms/graphtheory/TspDynamicProgrammingRecursive.java) **- O(n22n)**
diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/BUILD b/src/main/java/com/williamfiset/algorithms/graphtheory/BUILD
index 4a5e0ef1f..6d006c116 100644
--- a/src/main/java/com/williamfiset/algorithms/graphtheory/BUILD
+++ b/src/main/java/com/williamfiset/algorithms/graphtheory/BUILD
@@ -160,13 +160,6 @@ java_binary(
runtime_deps = [":graphtheory"],
)
-# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:KruskalsEdgeListPartialSortSolver
-java_binary(
- name = "KruskalsEdgeListPartialSortSolver",
- main_class = "com.williamfiset.algorithms.graphtheory.KruskalsEdgeListPartialSortSolver",
- runtime_deps = [":graphtheory"],
-)
-
# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:LazyPrimsAdjacencyList
java_binary(
name = "LazyPrimsAdjacencyList",
@@ -181,12 +174,6 @@ java_binary(
runtime_deps = [":graphtheory"],
)
-# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:TarjanAdjacencyMatrix
-java_binary(
- name = "TarjanAdjacencyMatrix",
- main_class = "com.williamfiset.algorithms.graphtheory.TarjanAdjacencyMatrix",
- runtime_deps = [":graphtheory"],
-)
# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:TarjanSccSolverAdjacencyList
java_binary(
diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/KruskalsEdgeListPartialSortSolver.java b/src/main/java/com/williamfiset/algorithms/graphtheory/KruskalsEdgeListPartialSortSolver.java
deleted file mode 100644
index b3a651014..000000000
--- a/src/main/java/com/williamfiset/algorithms/graphtheory/KruskalsEdgeListPartialSortSolver.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/**
- * An implementation of Kruskal's MST algorithm with lazy sorting.
- *
- *
Tested against: - https://open.kattis.com/problems/minspantree
- *
- *
Time Complexity: O(Elog(E))
- *
- * @author William Fiset, william.alexandre.fiset@gmail.com
- */
-package com.williamfiset.algorithms.graphtheory;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.PriorityQueue;
-
-public class KruskalsEdgeListPartialSortSolver {
-
- static class Edge implements Comparable {
- int u, v, cost;
-
- // 'u' and 'v' are nodes indexes and 'cost' is the cost of taking this edge.
- public Edge(int u, int v, int cost) {
- this.u = u;
- this.v = v;
- this.cost = cost;
- }
-
- // Sort edges based on cost.
- @Override
- public int compareTo(Edge other) {
- return cost - other.cost;
- }
- }
-
- // Inputs
- private int n;
- private List edges;
-
- // Internal
- private boolean solved;
- private boolean mstExists;
-
- // Outputs
- private Edge[] mst;
- private long mstCost;
-
- // edges - A list of undirected edges.
- // n - The number of nodes in the input graph.
- public KruskalsEdgeListPartialSortSolver(List edges, int n) {
- if (edges == null || n <= 1) throw new IllegalArgumentException();
- this.edges = edges;
- this.n = n;
- }
-
- // Gets the Minimum Spanning Tree (MST) of the input graph or null if no MST.
- public Edge[] getMst() {
- kruskals();
- return mstExists ? mst : null;
- }
-
- // Gets the Minimum Spanning Tree (MST) cost or null if no MST exists.
- public Long getMstCost() {
- kruskals();
- return mstExists ? mstCost : null;
- }
-
- private void kruskals() {
- if (solved) return;
-
- // Heapify operation in constructor transforms list of edges into a binary heap in O(n)
- PriorityQueue pq = new PriorityQueue<>(edges);
- UnionFind uf = new UnionFind(n);
-
- int index = 0;
- mst = new Edge[n - 1];
-
- while (!pq.isEmpty()) {
- // Use heap to poll the next cheapest edge. Polling avoids the need to sort
- // the edges before loop in the event that the algorithm terminates early.
- Edge edge = pq.poll();
-
- // Skip this edge to avoid creating a cycle in MST.
- if (uf.connected(edge.u, edge.v)) continue;
-
- // Include this edge
- uf.union(edge.u, edge.v);
- mstCost += edge.cost;
- mst[index++] = edge;
-
- // Stop early if we found a MST that includes all the nodes.
- if (uf.size(0) == n) break;
- }
-
- mstExists = (uf.size(0) == n);
- solved = true;
- }
-
- // Union find data structure
- private static class UnionFind {
- private int[] id, sz;
-
- public UnionFind(int n) {
- id = new int[n];
- sz = new int[n];
- for (int i = 0; i < n; i++) {
- id[i] = i;
- sz[i] = 1;
- }
- }
-
- public int find(int p) {
- int root = p;
- while (root != id[root]) root = id[root];
- // Path compression
- while (p != root) {
- int next = id[p];
- id[p] = root;
- p = next;
- }
- return root;
- }
-
- public boolean connected(int p, int q) {
- return find(p) == find(q);
- }
-
- public int size(int p) {
- return sz[find(p)];
- }
-
- public void union(int p, int q) {
- int root1 = find(p);
- int root2 = find(q);
- if (root1 == root2) return;
- if (sz[root1] < sz[root2]) {
- sz[root2] += sz[root1];
- id[root1] = root2;
- } else {
- sz[root1] += sz[root2];
- id[root2] = root1;
- }
- }
- }
-
- /* Usage example: */
-
- public static void main(String[] args) {
- int numNodes = 10;
- List edges = new ArrayList<>();
-
- edges.add(new Edge(0, 1, 5));
- edges.add(new Edge(1, 2, 4));
- edges.add(new Edge(2, 9, 2));
- edges.add(new Edge(0, 4, 1));
- edges.add(new Edge(0, 3, 4));
- edges.add(new Edge(1, 3, 2));
- edges.add(new Edge(2, 7, 4));
- edges.add(new Edge(2, 8, 1));
- edges.add(new Edge(9, 8, 0));
- edges.add(new Edge(4, 5, 1));
- edges.add(new Edge(5, 6, 7));
- edges.add(new Edge(6, 8, 4));
- edges.add(new Edge(4, 3, 2));
- edges.add(new Edge(5, 3, 5));
- edges.add(new Edge(3, 6, 11));
- edges.add(new Edge(6, 7, 1));
- edges.add(new Edge(3, 7, 2));
- edges.add(new Edge(7, 8, 6));
-
- KruskalsEdgeListPartialSortSolver solver;
- solver = new KruskalsEdgeListPartialSortSolver(edges, numNodes);
- Long cost = solver.getMstCost();
-
- if (cost == null) {
- System.out.println("No MST does not exists");
- } else {
- System.out.println("MST cost: " + cost);
- for (Edge e : solver.getMst()) {
- System.out.println(String.format("Used edge (%d, %d) with cost: %d", e.u, e.v, e.cost));
- }
- }
-
- // Output:
- // MST cost: 14
- // Used edge (9, 8) with cost: 0
- // Used edge (2, 8) with cost: 1
- // Used edge (6, 7) with cost: 1
- // Used edge (0, 4) with cost: 1
- // Used edge (4, 5) with cost: 1
- // Used edge (3, 7) with cost: 2
- // Used edge (4, 3) with cost: 2
- // Used edge (1, 3) with cost: 2
- // Used edge (1, 2) with cost: 4
-
- }
-}
diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/TarjanAdjacencyMatrix.java b/src/main/java/com/williamfiset/algorithms/graphtheory/TarjanAdjacencyMatrix.java
deleted file mode 100644
index a053ec2cc..000000000
--- a/src/main/java/com/williamfiset/algorithms/graphtheory/TarjanAdjacencyMatrix.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * An implementation of Tarjan's SCC algorithm for a directed graph. Time Complexity: O(V^2)
- *
- * @author Micah Stairs
- */
-package com.williamfiset.algorithms.graphtheory;
-
-import java.util.*;
-
-class TarjanAdjacencyMatrix {
-
- public static void main(String[] args) {
-
- // As an example we create a graph with four strongly connected components
-
- final int NUM_NODES = 10;
-
- boolean[][] adjMatrix = new boolean[NUM_NODES][NUM_NODES];
-
- // SCC 1 with nodes 0,1,2
- adjMatrix[0][1] = true;
- adjMatrix[1][2] = true;
- adjMatrix[2][0] = true;
-
- // SCC 2 with nodes 3,4,5,6
- adjMatrix[5][4] = true;
- adjMatrix[5][6] = true;
- adjMatrix[3][5] = true;
- adjMatrix[4][3] = true;
- adjMatrix[4][5] = true;
- adjMatrix[6][4] = true;
-
- // SCC 3 with nodes 7,8
- adjMatrix[7][8] = true;
- adjMatrix[8][7] = true;
-
- // SCC 4 is node 9 all alone by itself
- // Add a few more edges to make things interesting
- adjMatrix[1][5] = true;
- adjMatrix[1][7] = true;
- adjMatrix[2][7] = true;
- adjMatrix[6][8] = true;
- adjMatrix[9][8] = true;
- adjMatrix[9][4] = true;
-
- Tarjan sccs = new Tarjan(adjMatrix);
-
- System.out.println(
- "Strong connected component count: " + sccs.countStronglyConnectedComponents());
- System.out.println(
- "Strong connected components:\n" + Arrays.toString(sccs.getStronglyConnectedComponents()));
-
- // Output:
- // Strong connected component count: 4
- // Strong connected components:
- // [2, 2, 2, 1, 1, 1, 1, 0, 0, 3]
-
- }
-
- // Tarjan is used to find/count the Strongly Connected
- // Components (SCCs) in a directed graph in O(V+E).
- static class Tarjan {
-
- private int n, pre, count = 0;
- private int[] id, low;
- private boolean[] marked;
- private boolean[][] adj;
- private Stack stack = new Stack<>();
-
- // Tarjan input requires an NxN adjacency matrix
- public Tarjan(boolean[][] adj) {
- n = adj.length;
- this.adj = adj;
- marked = new boolean[n];
- id = new int[n];
- low = new int[n];
- for (int u = 0; u < n; u++) if (!marked[u]) dfs(u);
- }
-
- private void dfs(int u) {
- marked[u] = true;
- low[u] = pre++;
- int min = low[u];
- stack.push(u);
- for (int v = 0; v < n; v++) {
- if (adj[u][v]) {
- if (!marked[v]) dfs(v);
- if (low[v] < min) min = low[v];
- }
- }
- if (min < low[u]) {
- low[u] = min;
- return;
- }
- int v;
- do {
- v = stack.pop();
- id[v] = count;
- low[v] = n;
- } while (v != u);
- count++;
- }
-
- // Returns the id array with the strongly connected components.
- // If id[i] == id[j] then nodes i and j are part of the same strongly connected component.
- public int[] getStronglyConnectedComponents() {
- return id.clone();
- }
-
- // Returns the number of strongly connected components in this graph
- public int countStronglyConnectedComponents() {
- return count;
- }
- }
-}
diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/TarjanSccSolverAdjacencyList.java b/src/main/java/com/williamfiset/algorithms/graphtheory/TarjanSccSolverAdjacencyList.java
index 18f547b43..9e1a6bb0e 100644
--- a/src/main/java/com/williamfiset/algorithms/graphtheory/TarjanSccSolverAdjacencyList.java
+++ b/src/main/java/com/williamfiset/algorithms/graphtheory/TarjanSccSolverAdjacencyList.java
@@ -1,5 +1,5 @@
/**
- * An implementation of Tarjan's Strongly Connected Components algorithm using an adjacency list.
+ * Implementation of Tarjan's Strongly Connected Components algorithm using an adjacency list.
*
* Verified against:
*
@@ -9,7 +9,9 @@
*
https://www.hackerearth.com/practice/algorithms/graphs/strongly-connected-components/tutorial
*
*
- * Time complexity: O(V+E)
+ *
Time: O(V + E)
+ *
+ *
Space: O(V)
*
* @author William Fiset, william.alexandre.fiset@gmail.com
*/
@@ -21,51 +23,62 @@
public class TarjanSccSolverAdjacencyList {
- private int n;
- private List> graph;
+ private final int n;
+ private final List> graph;
private boolean solved;
private int sccCount, id;
- private boolean[] visited;
+ private boolean[] onStack;
private int[] ids, low, sccs;
private Deque stack;
private static final int UNVISITED = -1;
+ /**
+ * Creates a Tarjan SCC solver for the given directed graph.
+ *
+ * @param graph adjacency list representation of a directed graph.
+ * @throws IllegalArgumentException if the graph is null.
+ */
public TarjanSccSolverAdjacencyList(List> graph) {
- if (graph == null) throw new IllegalArgumentException("Graph cannot be null.");
+ if (graph == null)
+ throw new IllegalArgumentException("Graph cannot be null.");
n = graph.size();
this.graph = graph;
}
- // Returns the number of strongly connected components in the graph.
+ /** Returns the number of strongly connected components in the graph. */
public int sccCount() {
- if (!solved) solve();
+ if (!solved)
+ solve();
return sccCount;
}
- // Get the connected components of this graph. If two indexes
- // have the same value then they're in the same SCC.
+ /**
+ * Returns the SCC id for each node. If two nodes have the same value, they belong to the same
+ * SCC.
+ */
public int[] getSccs() {
- if (!solved) solve();
+ if (!solved)
+ solve();
return sccs;
}
+ /** Runs Tarjan's algorithm. */
public void solve() {
- if (solved) return;
+ if (solved)
+ return;
ids = new int[n];
low = new int[n];
sccs = new int[n];
- visited = new boolean[n];
+ onStack = new boolean[n];
stack = new ArrayDeque<>();
Arrays.fill(ids, UNVISITED);
- for (int i = 0; i < n; i++) {
- if (ids[i] == UNVISITED) {
+ for (int i = 0; i < n; i++)
+ if (ids[i] == UNVISITED)
dfs(i);
- }
- }
solved = true;
}
@@ -73,59 +86,41 @@ public void solve() {
private void dfs(int at) {
ids[at] = low[at] = id++;
stack.push(at);
- visited[at] = true;
+ onStack[at] = true;
for (int to : graph.get(at)) {
- if (ids[to] == UNVISITED) {
+ if (ids[to] == UNVISITED)
dfs(to);
- }
- if (visited[to]) {
+ if (onStack[to])
low[at] = min(low[at], low[to]);
- }
- /*
- TODO(william): investigate whether the proper way to update the lowlinks
- is the following bit of code. From my experience this doesn't seem to
- matter if the output is placed in a separate output array, but this needs
- further investigation.
-
- if (ids[to] == UNVISITED) {
- dfs(to);
- low[at] = min(low[at], low[to]);
- }
- if (visited[to]) {
- low[at] = min(low[at], ids[to]);
- }
- */
-
}
- // On recursive callback, if we're at the root node (start of SCC)
- // empty the seen stack until back to root.
+ // If we're at the root of an SCC, pop all nodes in this component off the stack.
if (ids[at] == low[at]) {
for (int node = stack.pop(); ; node = stack.pop()) {
- visited[node] = false;
+ onStack[node] = false;
sccs[node] = sccCount;
- if (node == at) break;
+ if (node == at)
+ break;
}
sccCount++;
}
}
- // Initializes adjacency list with n nodes.
+ /** Creates an adjacency list with n nodes. */
public static List> createGraph(int n) {
List> graph = new ArrayList<>(n);
- for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
+ for (int i = 0; i < n; i++)
+ graph.add(new ArrayList<>());
return graph;
}
- // Adds a directed edge from node 'from' to node 'to'
+ /** Adds a directed edge from node 'from' to node 'to'. */
public static void addEdge(List> graph, int from, int to) {
graph.get(from).add(to);
}
- /* Example usage: */
-
- public static void main(String[] arg) {
+ public static void main(String[] args) {
int n = 8;
List> graph = createGraph(n);
@@ -148,15 +143,9 @@ public static void main(String[] arg) {
int[] sccs = solver.getSccs();
Map> multimap = new HashMap<>();
for (int i = 0; i < n; i++) {
- if (!multimap.containsKey(sccs[i])) multimap.put(sccs[i], new ArrayList<>());
- multimap.get(sccs[i]).add(i);
+ multimap.computeIfAbsent(sccs[i], k -> new ArrayList<>()).add(i);
}
- // Prints:
- // Number of Strongly Connected Components: 3
- // Nodes: [0, 1, 2] form a Strongly Connected Component.
- // Nodes: [3, 7] form a Strongly Connected Component.
- // Nodes: [4, 5, 6] form a Strongly Connected Component.
System.out.printf("Number of Strongly Connected Components: %d\n", solver.sccCount());
for (List scc : multimap.values()) {
System.out.println("Nodes: " + scc + " form a Strongly Connected Component.");
diff --git a/src/main/java/com/williamfiset/algorithms/graphtheory/TopologicalSortAdjacencyList.java b/src/main/java/com/williamfiset/algorithms/graphtheory/TopologicalSortAdjacencyList.java
index 71d6233ad..cc9802056 100644
--- a/src/main/java/com/williamfiset/algorithms/graphtheory/TopologicalSortAdjacencyList.java
+++ b/src/main/java/com/williamfiset/algorithms/graphtheory/TopologicalSortAdjacencyList.java
@@ -1,11 +1,16 @@
/**
- * This topological sort implementation takes an adjacency list of an acyclic graph and returns an
- * array with the indexes of the nodes in a (non unique) topological order which tells you how to
- * process the nodes in the graph. More precisely from wiki: A topological ordering is a linear
- * ordering of its vertices such that for every directed edge uv from vertex u to vertex v, u comes
- * before v in the ordering.
+ * Topological sort implementation using DFS on an adjacency list of a Directed Acyclic Graph (DAG).
+ * Returns an array with the node indexes in a (non-unique) topological order: for every directed
+ * edge u -> v, u comes before v in the ordering.
*
- * Time Complexity: O(V + E)
+ *
Also includes a method to find shortest paths in a DAG using the topological ordering.
+ *
+ *
NOTE: An arguably simpler approach to topological sorting is Kahn's algorithm, which uses
+ * in-degree counting and BFS instead of DFS. See {@link Kahns}.
+ *
+ *
Time: O(V + E)
+ *
+ *
Space: O(V + E)
*
* @author William Fiset, william.alexandre.fiset@gmail.com
*/
@@ -15,95 +20,90 @@
public class TopologicalSortAdjacencyList {
- // Helper Edge class to describe edges in the graph
static class Edge {
int from, to, weight;
- public Edge(int f, int t, int w) {
- from = f;
- to = t;
- weight = w;
+ public Edge(int from, int to, int weight) {
+ this.from = from;
+ this.to = to;
+ this.weight = weight;
}
}
- // Helper method that performs a depth first search on the graph to give
- // us the topological ordering we want. Instead of maintaining a stack
- // of the nodes we see we simply place them inside the ordering array
- // in reverse order for simplicity.
- private static int dfs(
- int i, int at, boolean[] visited, int[] ordering, Map> graph) {
-
- visited[at] = true;
-
- List edges = graph.get(at);
-
- if (edges != null)
- for (Edge edge : edges) if (!visited[edge.to]) i = dfs(i, edge.to, visited, ordering, graph);
-
- ordering[i] = at;
- return i - 1;
- }
-
- // Finds a topological ordering of the nodes in a Directed Acyclic Graph (DAG)
- // The input to this function is an adjacency list for a graph and the number
- // of nodes in the graph.
- //
- // NOTE: 'numNodes' is not necessarily the number of nodes currently present
- // in the adjacency list since you can have singleton nodes with no edges which
- // wouldn't be present in the adjacency list but are still part of the graph!
- //
+ /**
+ * Finds a topological ordering of the nodes in a DAG.
+ *
+ * NOTE: {@code numNodes} is not necessarily the number of nodes in the adjacency list since
+ * singleton nodes with no edges wouldn't be present but are still part of the graph.
+ *
+ * @param graph adjacency list mapping node index to outgoing edges.
+ * @param numNodes total number of nodes in the graph.
+ * @return array of node indexes in topological order.
+ */
public static int[] topologicalSort(Map> graph, int numNodes) {
-
int[] ordering = new int[numNodes];
boolean[] visited = new boolean[numNodes];
int i = numNodes - 1;
for (int at = 0; at < numNodes; at++)
- if (!visited[at]) i = dfs(i, at, visited, ordering, graph);
+ if (!visited[at])
+ i = dfs(i, at, visited, ordering, graph);
return ordering;
}
- // A useful application of the topological sort is to find the shortest path
- // between two nodes in a Directed Acyclic Graph (DAG). Given an adjacency list
- // this method finds the shortest path to all nodes starting at 'start'
- //
- // NOTE: 'numNodes' is not necessarily the number of nodes currently present
- // in the adjacency list since you can have singleton nodes with no edges which
- // wouldn't be present in the adjacency list but are still part of the graph!
- //
- public static Integer[] dagShortestPath(Map> graph, int start, int numNodes) {
+ // Places nodes into the ordering array in reverse DFS post-order.
+ private static int dfs(
+ int i, int at, boolean[] visited, int[] ordering, Map> graph) {
+ visited[at] = true;
+
+ List edges = graph.get(at);
+ if (edges != null)
+ for (Edge edge : edges)
+ if (!visited[edge.to])
+ i = dfs(i, edge.to, visited, ordering, graph);
+
+ ordering[i] = at;
+ return i - 1;
+ }
+ /**
+ * Finds the shortest path from {@code start} to all other reachable nodes in a DAG.
+ *
+ * NOTE: {@code numNodes} is not necessarily the number of nodes in the adjacency list since
+ * singleton nodes with no edges wouldn't be present but are still part of the graph.
+ *
+ * @param graph adjacency list mapping node index to outgoing edges.
+ * @param start the source node.
+ * @param numNodes total number of nodes in the graph.
+ * @return array of shortest distances, null for unreachable nodes.
+ */
+ public static Integer[] dagShortestPath(
+ Map> graph, int start, int numNodes) {
int[] topsort = topologicalSort(graph, numNodes);
Integer[] dist = new Integer[numNodes];
dist[start] = 0;
for (int i = 0; i < numNodes; i++) {
-
int nodeIndex = topsort[i];
- if (dist[nodeIndex] != null) {
- List adjacentEdges = graph.get(nodeIndex);
- if (adjacentEdges != null) {
- for (Edge edge : adjacentEdges) {
-
- int newDist = dist[nodeIndex] + edge.weight;
- if (dist[edge.to] == null) dist[edge.to] = newDist;
- else dist[edge.to] = Math.min(dist[edge.to], newDist);
- }
- }
+ if (dist[nodeIndex] == null)
+ continue;
+ for (Edge edge : graph.getOrDefault(nodeIndex, Collections.emptyList())) {
+ int newDist = dist[nodeIndex] + edge.weight;
+ if (dist[edge.to] == null || newDist < dist[edge.to])
+ dist[edge.to] = newDist;
}
}
return dist;
}
- // Example usage of topological sort
public static void main(String[] args) {
-
- // Graph setup
final int N = 7;
Map> graph = new HashMap<>();
- for (int i = 0; i < N; i++) graph.put(i, new ArrayList<>());
+ for (int i = 0; i < N; i++)
+ graph.put(i, new ArrayList<>());
+
graph.get(0).add(new Edge(0, 1, 3));
graph.get(0).add(new Edge(0, 2, 2));
graph.get(0).add(new Edge(0, 5, 3));
@@ -115,18 +115,10 @@ public static void main(String[] args) {
graph.get(5).add(new Edge(5, 4, 7));
int[] ordering = topologicalSort(graph, N);
+ System.out.println(Arrays.toString(ordering));
- // // Prints: [6, 0, 5, 1, 2, 3, 4]
- System.out.println(java.util.Arrays.toString(ordering));
-
- // Finds all the shortest paths starting at node 0
Integer[] dists = dagShortestPath(graph, 0, N);
-
- // Find the shortest path from 0 to 4 which is 8.0
- System.out.println(dists[4]);
-
- // Find the shortest path from 0 to 6 which
- // is null since 6 is not reachable!
- System.out.println(dists[6]);
+ System.out.println(dists[4]); // 8
+ System.out.println(dists[6]); // null (unreachable)
}
}