Generated by Claude Code.
Dijkstra's algorithm finds the shortest path from a source node to all other nodes in a weighted graph with non-negative edge weights. This template provides correct, production-ready implementations for competitive programming and LeetCode problems.
- Time Complexity:
O((V + E) log V)whereV= vertices,E= edges - Space Complexity:
O(V + E)for adjacency list representation
This is the most commonly used implementation that handles duplicate entries in the priority queue by skipping stale entries.
fun dijkstra(n: Int, graph: Array<MutableList<Pair<Int, Int>>>, source: Int): IntArray {
val dist = IntArray(n) { Int.MAX_VALUE }
val pq = PriorityQueue<Pair<Int, Int>>(compareBy { it.first }) // (distance, node)
dist[source] = 0
pq.offer(0 to source)
while (pq.isNotEmpty()) {
val (d, u) = pq.poll()
// Skip if we've already found a better path
if (d > dist[u]) continue
// Relax all adjacent edges
for ((v, weight) in graph[u]) {
val newDist = dist[u] + weight
if (newDist < dist[v]) {
dist[v] = newDist
pq.offer(newDist to v)
}
}
}
return dist
}This implementation uses a visited set to ensure each node is processed only once. Slightly more memory but cleaner logic.
fun dijkstraWithVisited(n: Int, graph: Array<MutableList<Pair<Int, Int>>>, source: Int): IntArray {
val dist = IntArray(n) { Int.MAX_VALUE }
val visited = BooleanArray(n)
val pq = PriorityQueue<Pair<Int, Int>>(compareBy { it.first })
dist[source] = 0
pq.offer(0 to source)
while (pq.isNotEmpty()) {
val (d, u) = pq.poll()
if (visited[u]) continue
visited[u] = true
for ((v, weight) in graph[u]) {
if (!visited[v] && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight
pq.offer(dist[v] to v)
}
}
}
return dist
}This is a comprehensive template that handles common variations and optimizations needed for competitive programming.
class Solution {
fun dijkstra(n: Int, edges: Array<IntArray>, source: Int, target: Int = -1): IntArray {
// Build adjacency list
val graph = Array(n) { mutableListOf<Pair<Int, Int>>() }
for ((u, v, w) in edges) {
graph[u].add(v to w)
// Add this line for undirected graphs:
// graph[v].add(u to w)
}
val dist = IntArray(n) { Int.MAX_VALUE }
val pq = PriorityQueue<Pair<Int, Int>>(compareBy { it.first })
dist[source] = 0
pq.offer(0 to source)
while (pq.isNotEmpty()) {
val (d, u) = pq.poll()
// Early termination if target found
if (target != -1 && u == target) break
// Skip stale entries
if (d > dist[u]) continue
for ((v, weight) in graph[u]) {
val newDist = dist[u] + weight
if (newDist < dist[v]) {
dist[v] = newDist
pq.offer(newDist to v)
}
}
}
return dist
}
}- Store pairs as
(distance, node)not(node, distance) - Use
compareBy { it.first }to sort by distance - Kotlin's PriorityQueue is a min-heap by default
if (d > dist[u]) continueThis line is crucial! It skips processing nodes that have already been processed with a better distance, avoiding unnecessary work.
val newDist = dist[u] + weight
if (newDist < dist[v]) {
dist[v] = newDist
pq.offer(newDist to v)
}Always check if the new path is better before updating and adding to queue.
- Use
Array<MutableList<Pair<Int, Int>>>wherePair(neighbor, weight) - For undirected graphs, add edges in both directions
- For 0-indexed problems, use nodes 0 to n-1
- For 1-indexed problems, use nodes 1 to n (waste index 0)
fun dijkstraWithPath(n: Int, graph: Array<MutableList<Pair<Int, Int>>>, source: Int): Pair<IntArray, Array<Int?>> {
val dist = IntArray(n) { Int.MAX_VALUE }
val parent = Array<Int?>(n) { null }
val pq = PriorityQueue<Pair<Int, Int>>(compareBy { it.first })
dist[source] = 0
pq.offer(0 to source)
while (pq.isNotEmpty()) {
val (d, u) = pq.poll()
if (d > dist[u]) continue
for ((v, weight) in graph[u]) {
val newDist = dist[u] + weight
if (newDist < dist[v]) {
dist[v] = newDist
parent[v] = u
pq.offer(newDist to v)
}
}
}
return dist to parent
}
fun reconstructPath(parent: Array<Int?>, target: Int): List<Int> {
val path = mutableListOf<Int>()
var current: Int? = target
while (current != null) {
path.add(current)
current = parent[current]
}
return path.reversed()
}fun dijkstraToTarget(n: Int, graph: Array<MutableList<Pair<Int, Int>>>, source: Int, target: Int): Int {
val dist = IntArray(n) { Int.MAX_VALUE }
val pq = PriorityQueue<Pair<Int, Int>>(compareBy { it.first })
dist[source] = 0
pq.offer(0 to source)
while (pq.isNotEmpty()) {
val (d, u) = pq.poll()
if (u == target) return d
if (d > dist[u]) continue
for ((v, weight) in graph[u]) {
val newDist = dist[u] + weight
if (newDist < dist[v]) {
dist[v] = newDist
pq.offer(newDist to v)
}
}
}
return -1 // No path found
}- Wrong pair order: Use
(distance, node)not(node, distance) - Missing stale check: Always include
if (d > dist[u]) continue - Int overflow: Be careful with
Int.MAX_VALUE + weight - Graph indexing: Match your graph indexing (0-based vs 1-based) with problem requirements
- Forgetting edge cases: Handle single node, no edges, unreachable nodes
- ✅ Non-negative edge weights
- ✅ Single source shortest path
- ✅ Need shortest distances to all nodes or specific target
- ❌ Negative edge weights (use Bellman-Ford instead)
- ❌ Unweighted graphs (use BFS instead, it's simpler)
This template should handle 99% of Dijkstra problems you'll encounter in competitive programming!