Skip to content

Commit b71af5a

Browse files
williamfisetclaude
andauthored
Refactor Dijkstra shortest path implementations: clean up and simplify (williamfiset#1302)
* Refactor DijkstrasShortestPathAdjacencyList: remove Comparator, simplify Replace the Comparator field and constructor with Comparable on Node, simplify reconstructPath to use LinkedList.addFirst instead of reverse, and unbox Integer loop variable to int. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove PQ initial capacity, add test coverage for Dijkstra adjacency list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor Dijkstra adjacency list: clean up, simplify, apply style rules Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor Dijkstra DHeap variant: clean up, simplify, apply style rules Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add test coverage for DijkstrasShortestPathAdjacencyListWithDHeap Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent eba25c9 commit b71af5a

File tree

5 files changed

+417
-125
lines changed

5 files changed

+417
-125
lines changed

src/main/java/com/williamfiset/algorithms/graphtheory/DijkstrasShortestPathAdjacencyList.java

Lines changed: 43 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,13 @@
1111

1212
import java.util.ArrayList;
1313
import java.util.Arrays;
14-
import java.util.Collections;
15-
import java.util.Comparator;
14+
import java.util.LinkedList;
1615
import java.util.List;
1716
import java.util.PriorityQueue;
1817

1918
public class DijkstrasShortestPathAdjacencyList {
2019

21-
// Small epsilon value to comparing double values.
22-
private static final double EPS = 1e-6;
23-
24-
// An edge class to represent a directed edge
25-
// between two nodes with a certain cost.
20+
// Represents a directed edge between two nodes with a certain cost.
2621
public static class Edge {
2722
double cost;
2823
int from, to;
@@ -34,115 +29,100 @@ public Edge(int from, int to, double cost) {
3429
}
3530
}
3631

37-
// Node class to track the nodes to visit while running Dijkstra's
38-
public static class Node {
32+
// Represents a node-distance pair for the priority queue.
33+
public static class Node implements Comparable<Node> {
3934
int id;
4035
double value;
4136

4237
public Node(int id, double value) {
4338
this.id = id;
4439
this.value = value;
4540
}
41+
42+
@Override
43+
public int compareTo(Node other) {
44+
return Double.compare(this.value, other.value);
45+
}
4646
}
4747

48-
private int n;
48+
private final int n;
4949
private double[] dist;
5050
private Integer[] prev;
51-
private List<List<Edge>> graph;
52-
53-
private Comparator<Node> comparator =
54-
new Comparator<Node>() {
55-
@Override
56-
public int compare(Node node1, Node node2) {
57-
if (Math.abs(node1.value - node2.value) < EPS) return 0;
58-
return (node1.value - node2.value) > 0 ? +1 : -1;
59-
}
60-
};
51+
private final List<List<Edge>> graph;
6152

6253
/**
63-
* Initialize the solver by providing the graph size and a starting node. Use the {@link #addEdge}
64-
* method to actually add edges to the graph.
54+
* Initialize the solver by providing the graph size. Use the {@link #addEdge} method to add edges
55+
* to the graph.
6556
*
6657
* @param n - The number of nodes in the graph.
6758
*/
6859
public DijkstrasShortestPathAdjacencyList(int n) {
6960
this.n = n;
70-
createEmptyGraph();
71-
}
72-
73-
public DijkstrasShortestPathAdjacencyList(int n, Comparator<Node> comparator) {
74-
this(n);
75-
if (comparator == null) throw new IllegalArgumentException("Comparator cannot be null");
76-
this.comparator = comparator;
61+
this.graph = new ArrayList<>(n);
62+
for (int i = 0; i < n; i++)
63+
graph.add(new ArrayList<>());
7764
}
7865

7966
/**
8067
* Adds a directed edge to the graph.
8168
*
8269
* @param from - The index of the node the directed edge starts at.
83-
* @param to - The index of the node the directed edge end at.
70+
* @param to - The index of the node the directed edge ends at.
8471
* @param cost - The cost of the edge.
8572
*/
8673
public void addEdge(int from, int to, int cost) {
8774
graph.get(from).add(new Edge(from, to, cost));
8875
}
8976

90-
// Use {@link #addEdge} method to add edges to the graph and use this method
91-
// to retrieve the constructed graph.
9277
public List<List<Edge>> getGraph() {
9378
return graph;
9479
}
9580

9681
/**
9782
* Reconstructs the shortest path (of nodes) from 'start' to 'end' inclusive.
9883
*
99-
* @return An array of nodes indexes of the shortest path from 'start' to 'end'. If 'start' and
100-
* 'end' are not connected then an empty array is returned.
84+
* @return An array of node indexes of the shortest path from 'start' to 'end'. If 'start' and
85+
* 'end' are not connected then an empty list is returned.
10186
*/
10287
public List<Integer> reconstructPath(int start, int end) {
103-
if (end < 0 || end >= n) throw new IllegalArgumentException("Invalid node index");
104-
if (start < 0 || start >= n) throw new IllegalArgumentException("Invalid node index");
105-
double dist = dijkstra(start, end);
106-
List<Integer> path = new ArrayList<>();
107-
if (dist == Double.POSITIVE_INFINITY) return path;
108-
for (Integer at = end; at != null; at = prev[at]) path.add(at);
109-
Collections.reverse(path);
88+
dijkstra(start, end);
89+
LinkedList<Integer> path = new LinkedList<>();
90+
if (dist[end] == Double.POSITIVE_INFINITY)
91+
return path;
92+
for (int at = end; at != start; at = prev[at])
93+
path.addFirst(at);
94+
path.addFirst(start);
11095
return path;
11196
}
11297

113-
// Run Dijkstra's algorithm on a directed graph to find the shortest path
114-
// from a starting node to an ending node. If there is no path between the
115-
// starting node and the destination node the returned value is set to be
116-
// Double.POSITIVE_INFINITY.
98+
/**
99+
* Runs Dijkstra's algorithm on a directed graph to find the shortest path from a starting node to
100+
* an ending node. If there is no path between the starting node and the destination node the
101+
* returned value is Double.POSITIVE_INFINITY.
102+
*/
117103
public double dijkstra(int start, int end) {
118-
// Maintain an array of the minimum distance to each node
119104
dist = new double[n];
120105
Arrays.fill(dist, Double.POSITIVE_INFINITY);
121106
dist[start] = 0;
122107

123108
// Keep a priority queue of the next most promising node to visit.
124-
PriorityQueue<Node> pq = new PriorityQueue<>(2 * n, comparator);
109+
PriorityQueue<Node> pq = new PriorityQueue<>();
125110
pq.offer(new Node(start, 0));
126111

127-
// Array used to track which nodes have already been visited.
128112
boolean[] visited = new boolean[n];
129113
prev = new Integer[n];
130114

131115
while (!pq.isEmpty()) {
132116
Node node = pq.poll();
133117
visited[node.id] = true;
134118

135-
// We already found a better path before we got to
136-
// processing this node so we can ignore it.
137-
if (dist[node.id] < node.value) continue;
138-
139-
List<Edge> edges = graph.get(node.id);
140-
for (int i = 0; i < edges.size(); i++) {
141-
Edge edge = edges.get(i);
119+
// We already found a better path before we got to processing this node.
120+
if (dist[node.id] < node.value)
121+
continue;
142122

143-
// You cannot get a shorter path by revisiting
144-
// a node you have already visited before.
145-
if (visited[edge.to]) continue;
123+
for (Edge edge : graph.get(node.id)) {
124+
if (visited[edge.to])
125+
continue;
146126

147127
// Relax edge by updating minimum cost if applicable.
148128
double newDist = dist[edge.from] + edge.cost;
@@ -152,18 +132,12 @@ public double dijkstra(int start, int end) {
152132
pq.offer(new Node(edge.to, dist[edge.to]));
153133
}
154134
}
155-
// Once we've visited all the nodes spanning from the end
156-
// node we know we can return the minimum distance value to
157-
// the end node because it cannot get any better after this point.
158-
if (node.id == end) return dist[end];
135+
136+
// Once we've processed the end node we can return early because the
137+
// distance cannot improve after this point.
138+
if (node.id == end)
139+
return dist[end];
159140
}
160-
// End node is unreachable
161141
return Double.POSITIVE_INFINITY;
162142
}
163-
164-
// Construct an empty graph with n nodes including the source and sink nodes.
165-
private void createEmptyGraph() {
166-
graph = new ArrayList<>(n);
167-
for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
168-
}
169143
}

0 commit comments

Comments
 (0)