|
2 | 2 |
|
3 | 3 | import java.util.HashMap; |
4 | 4 | import java.util.Map; |
5 | | -import java.util.NavigableSet; |
6 | | -import java.util.TreeSet; |
| 5 | +import java.util.PriorityQueue; |
| 6 | +import java.util.Comparator; |
7 | 7 | /** |
8 | 8 | * Dijkstra's algorithm,is a graph search algorithm that solves the |
9 | 9 | * single-source shortest path problem for a graph with nonnegative edge path |
|
12 | 12 | * <p> |
13 | 13 | * NOTE: The inputs to Dijkstra's algorithm are a directed and weighted graph |
14 | 14 | * consisting of 2 or more nodes, generally represented by an adjacency matrix |
15 | | - * or list, and a start node. |
| 15 | + * or list, and a start node. This implementation uses a binary heap |
| 16 | + * (Java's {@link PriorityQueue}) to achieve a time complexity of |
| 17 | + * O((V + E) log V) in practice. Practical use-cases include GPS routing and |
| 18 | + * network routing where all edge weights are non-negative. |
16 | 19 | * |
17 | 20 | * <p> |
18 | 21 | * Original source of code: |
@@ -182,46 +185,72 @@ public void dijkstra(String startName) { |
182 | 185 | return; |
183 | 186 | } |
184 | 187 | final Vertex source = graph.get(startName); |
185 | | - NavigableSet<Vertex> q = new TreeSet<>(); |
186 | 188 |
|
187 | | - // set-up vertices |
| 189 | + // initialize distances |
188 | 190 | for (Vertex v : graph.values()) { |
189 | 191 | v.previous = v == source ? source : null; |
190 | 192 | v.dist = v == source ? 0 : Integer.MAX_VALUE; |
191 | | - q.add(v); |
192 | 193 | } |
193 | 194 |
|
194 | | - dijkstra(q); |
195 | | - } |
| 195 | + // Priority queue of (vertex, knownDist) entries. We push new entries when |
| 196 | + // a shorter distance is found; stale entries are ignored when polled. |
| 197 | + PriorityQueue<NodeEntry> pq = new PriorityQueue<>(Comparator |
| 198 | + .comparingInt((NodeEntry e) -> e.dist) |
| 199 | + .thenComparing(e -> e.vertex.name)); |
| 200 | + |
| 201 | + pq.add(new NodeEntry(source, 0)); |
196 | 202 |
|
| 203 | + dijkstra(pq); |
| 204 | + } |
197 | 205 | /** |
198 | | - * Implementation of dijkstra's algorithm using a binary heap. |
| 206 | + * Implementation of dijkstra's algorithm using a priority queue of entries. |
199 | 207 | */ |
200 | | - private void dijkstra(final NavigableSet<Vertex> q) { |
201 | | - Vertex u; |
202 | | - Vertex v; |
203 | | - while (!q.isEmpty()) { |
204 | | - // vertex with shortest distance (first iteration will return source) |
205 | | - u = q.pollFirst(); |
| 208 | + private void dijkstra(final PriorityQueue<NodeEntry> pq) { |
| 209 | + while (!pq.isEmpty()) { |
| 210 | + final NodeEntry entry = pq.poll(); |
| 211 | + final Vertex u = entry.vertex; |
| 212 | + |
| 213 | + // ignore stale/popped entries |
| 214 | + if (entry.dist != u.dist) { |
| 215 | + continue; |
| 216 | + } |
| 217 | + |
206 | 218 | if (u.dist == Integer.MAX_VALUE) { |
207 | | - break; // we can ignore u (and any other remaining vertices) since they are |
208 | | - // unreachable |
| 219 | + break; // remaining vertices are unreachable |
209 | 220 | } |
| 221 | + |
210 | 222 | // look at distances to each neighbour |
211 | 223 | for (Map.Entry<Vertex, Integer> a : u.neighbours.entrySet()) { |
212 | | - v = a.getKey(); // the neighbour in this iteration |
| 224 | + final Vertex v = a.getKey(); // the neighbour in this iteration |
| 225 | + final int weight = a.getValue(); |
| 226 | + |
| 227 | + if (weight < 0) { |
| 228 | + throw new IllegalArgumentException("Graph contains negative edge weight: " + weight); |
| 229 | + } |
213 | 230 |
|
214 | | - final int alternateDist = u.dist + a.getValue(); |
| 231 | + final int alternateDist = u.dist + weight; |
215 | 232 | if (alternateDist < v.dist) { // shorter path to neighbour found |
216 | | - q.remove(v); |
217 | 233 | v.dist = alternateDist; |
218 | 234 | v.previous = u; |
219 | | - q.add(v); |
| 235 | + pq.add(new NodeEntry(v, alternateDist)); |
220 | 236 | } |
221 | 237 | } |
222 | 238 | } |
223 | 239 | } |
224 | 240 |
|
| 241 | + /** |
| 242 | + * Helper entry for the priority queue to avoid costly removals (no decrease-key). |
| 243 | + */ |
| 244 | + private static class NodeEntry { |
| 245 | + final Vertex vertex; |
| 246 | + final int dist; |
| 247 | + |
| 248 | + NodeEntry(Vertex vertex, int dist) { |
| 249 | + this.vertex = vertex; |
| 250 | + this.dist = dist; |
| 251 | + } |
| 252 | + } |
| 253 | + |
225 | 254 | /** |
226 | 255 | * Prints a path from the source to the specified vertex |
227 | 256 | */ |
|
0 commit comments