Shortest Path with Dijkstra
Problem
An undirected graph has n nodes (numbered 1 to n) and m weighted edges. The first line has n and m. The next m lines have u v w (an edge between u and v with the positive weight w). The last line has two nodes: the start s and the target t. Print the length of the shortest path from s to t, or -1 if t can not be reached.
Constraints
- 1 <= n <= 1000
- 0 <= m <= 5000
- 1 <= w <= 10^6
Examples
Input
4 4 1 2 1 2 3 2 1 3 5 3 4 1 1 4
Output
4
Solutions
Dijkstra's algorithm always finalizes the closest unfinished node. When all weights are positive, no later path can make that node closer. A priority queue finds the closest node quickly. The JavaScript and TypeScript versions sort a small list instead, which is simpler but slower for big graphs.
/*
* Problem : Shortest path length.
* Input : Graph edges, then s and t.
* Output : The distance or -1.
* Approach: Priority queue.
* Time : O((n + m) log n)
* Space : O(n + m)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// This method solves the task. It receives the input and returns the answer.
static long solve(String[] lines) {
String[] header = lines[0].split(" ");
int n = Integer.parseInt(header[0]);
int m = Integer.parseInt(header[1]);
// Step 1: Adjacency lists. For each node we keep its neighbours as {neighbour, weight}.
java.util.List<java.util.List<int[]>> graph = new java.util.ArrayList<>();
for (int i = 0; i <= n; i++) {
graph.add(new java.util.ArrayList<>());
}
for (int i = 1; i <= m; i++) {
String[] edge = lines[i].split(" ");
int u = Integer.parseInt(edge[0]);
int v = Integer.parseInt(edge[1]);
int w = Integer.parseInt(edge[2]);
graph.get(u).add(new int[] {v, w});
graph.get(v).add(new int[] {u, w});
}
String[] ends = lines[m + 1].split(" ");
int start = Integer.parseInt(ends[0]);
int target = Integer.parseInt(ends[1]);
// Step 2: distance[x] = the best known distance from the start. At first all are "infinite".
long[] distance = new long[n + 1];
java.util.Arrays.fill(distance, Long.MAX_VALUE);
distance[start] = 0;
// Step 3: A priority queue always gives the node with the smallest known distance.
java.util.PriorityQueue<long[]> queue = new java.util.PriorityQueue<>((x, y) -> Long.compare(x[0], y[0]));
queue.add(new long[] {0, start});
while (!queue.isEmpty()) {
long[] top = queue.poll();
int node = (int) top[1];
// Step 4: Skip old entries. A shorter distance for this node was already found.
if (top[0] > distance[node]) {
continue;
}
// Step 5: Relax the edges. If we find a shorter way to a neighbour, we save it and add the neighbour to the queue.
for (int[] edge : graph.get(node)) {
long candidate = top[0] + edge[1];
if (candidate < distance[edge[0]]) {
distance[edge[0]] = candidate;
queue.add(new long[] {candidate, edge[0]});
}
}
}
return distance[target] == Long.MAX_VALUE ? -1 : distance[target];
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
java.util.List<String> all = new java.util.ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
all.add(line);
}
}
String[] lines = all.toArray(new String[0]);
System.out.println(solve(lines));
}
}
Hints (2)
- Why does Dijkstra need positive weights?
- What does the priority queue return?