-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Dijkstra.java
145 lines (121 loc) · 6.64 KB
/
Dijkstra.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package com.jwetherell.algorithms.graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import com.jwetherell.algorithms.data_structures.Graph;
/**
* Dijkstra's shortest path. Only works on non-negative path weights. Returns a
* tuple of total cost of shortest path and the path.
* <p>
* Worst case: O(|E| + |V| log |V|)
* <p>
* @see <a href="https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm">Dijkstra's Algorithm (Wikipedia)</a>
* <br>
* @author Justin Wetherell <[email protected]>
*/
public class Dijkstra {
private Dijkstra() { }
public static Map<Graph.Vertex<Integer>, Graph.CostPathPair<Integer>> getShortestPaths(Graph<Integer> graph, Graph.Vertex<Integer> start) {
final Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>> paths = new HashMap<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>();
final Map<Graph.Vertex<Integer>, Graph.CostVertexPair<Integer>> costs = new HashMap<Graph.Vertex<Integer>, Graph.CostVertexPair<Integer>>();
getShortestPath(graph, start, null, paths, costs);
final Map<Graph.Vertex<Integer>, Graph.CostPathPair<Integer>> map = new HashMap<Graph.Vertex<Integer>, Graph.CostPathPair<Integer>>();
for (Graph.CostVertexPair<Integer> pair : costs.values()) {
int cost = pair.getCost();
Graph.Vertex<Integer> vertex = pair.getVertex();
List<Graph.Edge<Integer>> path = paths.get(vertex);
map.put(vertex, new Graph.CostPathPair<Integer>(cost, path));
}
return map;
}
public static Graph.CostPathPair<Integer> getShortestPath(Graph<Integer> graph, Graph.Vertex<Integer> start, Graph.Vertex<Integer> end) {
if (graph == null)
throw (new NullPointerException("Graph must be non-NULL."));
// Dijkstra's algorithm only works on positive cost graphs
final boolean hasNegativeEdge = checkForNegativeEdges(graph.getVertices());
if (hasNegativeEdge)
throw (new IllegalArgumentException("Negative cost Edges are not allowed."));
final Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>> paths = new HashMap<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>();
final Map<Graph.Vertex<Integer>, Graph.CostVertexPair<Integer>> costs = new HashMap<Graph.Vertex<Integer>, Graph.CostVertexPair<Integer>>();
return getShortestPath(graph, start, end, paths, costs);
}
private static Graph.CostPathPair<Integer> getShortestPath(Graph<Integer> graph,
Graph.Vertex<Integer> start, Graph.Vertex<Integer> end,
Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>> paths,
Map<Graph.Vertex<Integer>, Graph.CostVertexPair<Integer>> costs) {
if (graph == null)
throw (new NullPointerException("Graph must be non-NULL."));
if (start == null)
throw (new NullPointerException("start must be non-NULL."));
// Dijkstra's algorithm only works on positive cost graphs
boolean hasNegativeEdge = checkForNegativeEdges(graph.getVertices());
if (hasNegativeEdge)
throw (new IllegalArgumentException("Negative cost Edges are not allowed."));
for (Graph.Vertex<Integer> v : graph.getVertices())
paths.put(v, new ArrayList<Graph.Edge<Integer>>());
for (Graph.Vertex<Integer> v : graph.getVertices()) {
if (v.equals(start))
costs.put(v, new Graph.CostVertexPair<Integer>(0, v));
else
costs.put(v, new Graph.CostVertexPair<Integer>(Integer.MAX_VALUE, v));
}
final Queue<Graph.CostVertexPair<Integer>> unvisited = new PriorityQueue<Graph.CostVertexPair<Integer>>();
unvisited.add(costs.get(start));
while (!unvisited.isEmpty()) {
final Graph.CostVertexPair<Integer> pair = unvisited.remove();
final Graph.Vertex<Integer> vertex = pair.getVertex();
// Compute costs from current vertex to all reachable vertices which haven't been visited
for (Graph.Edge<Integer> e : vertex.getEdges()) {
final Graph.CostVertexPair<Integer> toPair = costs.get(e.getToVertex()); // O(1)
final Graph.CostVertexPair<Integer> lowestCostToThisVertex = costs.get(vertex); // O(1)
final int cost = lowestCostToThisVertex.getCost() + e.getCost();
if (toPair.getCost() == Integer.MAX_VALUE) {
// Haven't seen this vertex yet
// Need to remove the pair and re-insert, so the priority queue keeps it's invariants
unvisited.remove(toPair); // O(n)
toPair.setCost(cost);
unvisited.add(toPair); // O(log n)
// Update the paths
List<Graph.Edge<Integer>> set = paths.get(e.getToVertex()); // O(log n)
set.addAll(paths.get(e.getFromVertex())); // O(log n)
set.add(e);
} else if (cost < toPair.getCost()) {
// Found a shorter path to a reachable vertex
// Need to remove the pair and re-insert, so the priority queue keeps it's invariants
unvisited.remove(toPair); // O(n)
toPair.setCost(cost);
unvisited.add(toPair); // O(log n)
// Update the paths
List<Graph.Edge<Integer>> set = paths.get(e.getToVertex()); // O(log n)
set.clear();
set.addAll(paths.get(e.getFromVertex())); // O(log n)
set.add(e);
}
}
// Termination conditions
if (end != null && vertex.equals(end)) {
// We are looking for shortest path to a specific vertex, we found it.
break;
}
}
if (end != null) {
final Graph.CostVertexPair<Integer> pair = costs.get(end);
final List<Graph.Edge<Integer>> set = paths.get(end);
return (new Graph.CostPathPair<Integer>(pair.getCost(), set));
}
return null;
}
private static boolean checkForNegativeEdges(Collection<Graph.Vertex<Integer>> vertitices) {
for (Graph.Vertex<Integer> v : vertitices) {
for (Graph.Edge<Integer> e : v.getEdges()) {
if (e.getCost() < 0)
return true;
}
}
return false;
}
}