Graph · 12 of 43
Dijkstra algo
Find the cheapest way from home to every place on a map by always finishing the closest unfinished place first.
- Simple way
- time O(V^2)
room O(V) - Clever way
- time O((V + E) log V)
room O(V + E)
The problem, simply
You get a map of V places joined by two-way roads. Every road has a cost, like minutes of walking, and no cost is below zero. You also get a starting place, src.
For every place, give back the smallest total cost to get there from src. If a place cannot be reached, its answer stays "infinity".
A story to remember it
Picture it
Five places. The numbers on the roads are costs. The direct road from 0 to 1 costs 4, but going 0 to 2 to 1 costs only 1 + 2 = 3.
- looking now
Examples
A normal map
- In
V = 5, src = 0, edges = [[0,1,4],[0,2,1],[2,1,2],[1,3,1],[2,3,5],[3,4,3]]- Out
[0, 3, 1, 4, 7]
Place 4 is reached by 0, 2, 1, 3, 4 for 1 + 2 + 1 + 3 = 7.
The direct road is a trap
- In
V = 3, src = 0, edges = [[0,1,10],[0,2,1],[2,1,1]]- Out
[0, 2, 1]
The road straight to place 1 costs 10, but the detour through place 2 costs 2. Fewest roads is not the same as cheapest.
A place you cannot reach
- In
V = 3, src = 0, edges = [[0,1,5]]- Out
[0, 5, infinity]
No road touches place 2. Its cost never changes from the starting infinity. Many problems want -1 here instead.
Just home
- In
V = 1, src = 0, edges = []- Out
[0]
Home costs 0 to reach. The loop runs once and stops.
Way 1: the simple way
Keep a list of costs. In every round, scan all places to find the cheapest one that is not settled yet. Settle it, then try every road out of it.
- Set every cost to infinity, and the cost of
srcto 0. - Repeat
Vtimes: scan all places and pick the unsettled one with the smallest cost. - Mark it settled. For each road to a neighbor, if
cost[u] + wis smaller than the neighbor's cost, write the smaller number.
class DijkstraArray {
public static int[] dijkstra(int V, int[][] edges, int src) {
List<List<int[]>> adj = new ArrayList<>();
for (int i = 0; i < V; i++) adj.add(new ArrayList<>());
for (int[] e : edges) {
adj.get(e[0]).add(new int[]{e[1], e[2]});
adj.get(e[1]).add(new int[]{e[0], e[2]}); // two-way road
}
int[] dist = new int[V];
boolean[] settled = new boolean[V];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src] = 0;
for (int round = 0; round < V; round++) {
int u = -1;
for (int v = 0; v < V; v++) { // scan everyone for the cheapest
if (!settled[v] && (u == -1 || dist[v] < dist[u])) u = v;
}
if (dist[u] == Integer.MAX_VALUE) break; // the rest cannot be reached
settled[u] = true;
for (int[] nb : adj.get(u)) {
int v = nb[0], w = nb[1];
if (!settled[v] && dist[u] + w < dist[v]) dist[v] = dist[u] + w;
}
}
return dist;
}
}How long it takes: V rounds, and each round scans all V places, O(V^2). The roads add O(E) on top. Extra room: two arrays, O(V). On a very crowded map, where E is close to V^2, this is actually the best choice.
Way 2: the clever way
The slow part is the scan for the cheapest place. A min-heap, which is the pile of sticky notes, hands you the smallest note in O(log V) instead.
- Push the note
0:src, meaning cost 0 at placesrc. In the pictures a note3:1means "place 1 for cost 3". - Pop the smallest note
d:u. Ifdis bigger than the best cost we already wrote foru, the note is old. Throw it away. - Otherwise
uis settled. For each roadutovwith costw, ifd + wbeatsdist[v], write it and push a new noted + w : v. - Stop when the pile is empty.
The heap always hands us the closest unfinished place
01 / 09
Every place starts with cost ∞ except home, place 0, which costs 0. Put the note 0:0 (cost 0, place 0) in the heap.
- looking now
- done
- answer
- rejected
- just added
class DijkstraHeap {
public static int[] dijkstra(int V, int[][] edges, int src) {
List<List<int[]>> adj = new ArrayList<>();
for (int i = 0; i < V; i++) adj.add(new ArrayList<>());
for (int[] e : edges) {
adj.get(e[0]).add(new int[]{e[1], e[2]});
adj.get(e[1]).add(new int[]{e[0], e[2]});
}
int[] dist = new int[V];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src] = 0;
// each note is {cost, place}; the smallest cost comes out first
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> Integer.compare(a[0], b[0]));
pq.add(new int[]{0, src});
while (!pq.isEmpty()) {
int[] top = pq.poll();
int d = top[0], u = top[1];
if (d > dist[u]) continue; // an old note, throw it away
for (int[] nb : adj.get(u)) {
int v = nb[0], w = nb[1];
if (d + w < dist[v]) {
dist[v] = d + w;
pq.add(new int[]{dist[v], v}); // a new, better note
}
}
}
return dist;
}
public static void main(String[] args) {
int[][] edges = {{0, 1, 4}, {0, 2, 1}, {2, 1, 2}, {1, 3, 1}, {2, 3, 5}, {3, 4, 3}};
System.out.println(Arrays.toString(dijkstra(5, edges, 0))); // [0, 3, 1, 4, 7]
}
}How long it takes: every road can push at most one note, so the heap sees at most E notes, and each push or pop costs O(log E), which is O(log V). In total O((V + E) log V). Extra room: the road lists plus the heap, O(V + E).
Watch out for
- Forgetting the old-note check
if (d > dist[u]) continue. The answer stays right, but you redo work and can slow down badly on big graphs. - Writing the comparator as
a[0] - b[0]overflows when costs are nearInteger.MAX_VALUE. UseInteger.compare, orlongcosts. - Adding
dist[u] + wwhendist[u]is stillInteger.MAX_VALUEwraps to a negative number. The heap version never pops an unreached place, but the array version must check first. - One-way roads: add only
adj.get(u).add(...), not both directions.
What interviewers ask next
- How do you print the actual route? Keep
parent[v] = uevery time you improvev. Walk the parents back from the target and reverse. - Only one target? Stop as soon as you pop the target. Its cost is final at that moment.
- What if every road costs 0 or 1? Use 0-1 BFS with a deque: push 0-cost neighbors to the front and 1-cost neighbors to the back. It runs in
O(V + E). - Why does it fail with negative edges, but is fine with zero edges? Zero never makes a later note smaller than a settled one; a negative edge can.
- What about a limit on the number of roads used? Then the cheapest cost alone is not enough to settle a place. See "Cheapest flights within K stops".
Remember this
Grown-up words: places are vertices, roads are weighted edges, the pile of sticky notes is a priority queue (a min-heap), writing a better cost is relaxing an edge, throwing away old notes is lazy deletion, and the highlighted roads at the end form the shortest path tree.