Dijkstra’s Algorithm Using Priority Queue
Problem and examples
You are given a weighted, undirected, and connected graph with V vertices represented using an adjacency list. Each entry adj[i] contains a list of lists, where each sublist contains two integers j and weight, representing an edge from vertex i to vertex j with a given weight.
Given a source vertex S, your task is to find the shortest distances from S to all other vertices using Dijkstra’s Algorithm and return them as a list.
| Vertices | Adjacency List | Source | Shortest Distances |
|---|---|---|---|
| 3 | [ [ [1, 1], [2, 4] ], [ [0, 1], [2, 2] ], [ [0, 4], [1, 2] ] ] | 0 | [0, 1, 3] |
| 2 | [ [ [1, 5] ], [ [0, 5] ] ] | 1 | [5, 0] |
| 1 | [ [] ] | 0 | [0] |
| 4 | [ [ [1, 2] ], [ [0, 2], [2, 1] ], [ [1, 1], [3, 5] ], [ [2, 5] ] ] | 0 | [0, 2, 3, 8] |
How it works
Understanding the Problem
We are given a weighted graph and a starting node (source). Our goal is to find the shortest distance from this source to all other nodes in the graph.
In other words, for every node in the graph, we want to calculate the minimum possible sum of edge weights required to reach that node from the source.
This problem is a classic case of finding single-source shortest paths in graphs with non-negative weights, and Dijkstra’s Algorithm is an efficient way to solve it using a priority queue.
Step-by-Step Solution with Example
Step 1: Choose an Example
Let's consider the following weighted graph with 5 nodes (0 to 4), and the edges:
0 --(2)--> 1
0 --(4)--> 2
1 --(1)--> 2
1 --(7)--> 3
2 --(3)--> 4
3 --(1)--> 4
We want to find the shortest distance from node 0 to all other nodes.
Step 2: Initialize the Distance Array
Create a distance[] array and fill it with Infinity to represent that we don’t yet know the shortest path. Set the distance of the source node to 0.
distance = [0, ∞, ∞, ∞, ∞]
Step 3: Use a Min-Heap (Priority Queue)
Use a priority queue (min-heap) to always pick the next node with the smallest known distance. Initially, insert the source node with distance 0:
priorityQueue = [(0, 0)] // (distance, node)
Step 4: Start the Main Loop
While the priority queue is not empty:
- Extract the node with the smallest distance.
- Explore all its neighbors.
- If a shorter path to a neighbor is found, update the distance and insert it into the queue.
Step 5: Trace the Execution for Our Example
- Start with node 0, distance 0.
- Check edges: (0 → 1, weight 2), (0 → 2, weight 4)
- Update:
distance[1] = 2,distance[2] = 4 - Queue: [(2,1), (4,2)]
- Next node: 1, distance 2
- Check edges: (1 → 2, weight 1) →
2 + 1 = 3is better than current 4, updatedistance[2] = 3 - Also (1 → 3, weight 7) →
distance[3] = 9 - Queue: [(3,2), (4,2), (9,3)]
- Next node: 2, distance 3
- Check edge (2 → 4, weight 3) →
distance[4] = 6 - Queue: [(4,2), (9,3), (6,4)]
- Next node: 4, distance 6 → Already the best.
- Check edge (4 → 3, weight 1) →
6 + 1 = 7is better than 9 → updatedistance[3] = 7
Step 6: Final Distance Array
distance = [0, 2, 3, 7, 6]
This gives the shortest path from node 0 to every other node in the graph.
Edge Cases
- Disconnected Graph: If a node is not reachable from the source, its distance will remain
Infinity. - Zero Weight Edges: Dijkstra’s algorithm handles them correctly as long as weights are non-negative.
- Multiple Edges Between Nodes: Always consider the one with the minimum weight during edge relaxation.
- Self-loops: They can be ignored unless required for the specific application.
Finally
Dijkstra’s Algorithm is a powerful tool for finding the shortest paths in a graph with non-negative weights. Using a priority queue helps efficiently expand the closest node at every step. Always remember to initialize distances properly and carefully update them when a shorter path is found. This algorithm is widely used in routing, pathfinding, and network optimization problems.
- Initialize
dist[]withInfinity, setdist[S] = 0. - Create a priority queue (min-heap) and insert
{0, S}. - While the queue is not empty:
- Pop the vertex with the minimum distance from the queue.
- For each adjacent vertex
vof the current node: - If
dist[u] + weight < dist[v], updatedist[v]and add{dist[v], v}to the queue. - Return
dist[]as the shortest distances from sourceS.
Implementation and efficiency
C implementation
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>
#define V 3
int minDistance(int dist[], bool sptSet[]) {
int min = INT_MAX, min_index;
for (int v = 0; v < V; v++) {
if (!sptSet[v] && dist[v] <= min) {
min = dist[v], min_index = v;
}
}
return min_index;
}
void dijkstra(int graph[V][V], int src) {
int dist[V];
bool sptSet[V];
for (int i = 0; i < V; i++) {
dist[i] = INT_MAX, sptSet[i] = false;
}
dist[src] = 0;
for (int count = 0; count < V - 1; count++) {
int u = minDistance(dist, sptSet);
sptSet[u] = true;
for (int v = 0; v < V; v++) {
if (!sptSet[v] && graph[u][v] &&
dist[u] != INT_MAX &&
dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
printf("Shortest distances:\n");
for (int i = 0; i < V; i++) {
printf("%d ", dist[i]);
}
}
int main() {
int graph[V][V] = {
{0, 1, 4},
{1, 0, 2},
{4, 2, 0}
};
dijkstra(graph, 0);
return 0;
}C++ implementation
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef pair<int, int> pii;
vector<int> dijkstra(int V, vector<vector<pii>>& adj, int S) {
vector<int> dist(V, INT_MAX);
priority_queue<pii, vector<pii>, greater<pii>> pq;
dist[S] = 0;
pq.push({0, S});
while (!pq.empty()) {
int u = pq.top().second;
int d = pq.top().first;
pq.pop();
for (auto edge : adj[u]) {
int v = edge.first;
int weight = edge.second;
if (d + weight < dist[v]) {
dist[v] = d + weight;
pq.push({dist[v], v});
}
}
}
return dist;
}
int main() {
int V = 3;
vector<vector<pii>> adj(V);
adj[0] = {{1, 1}, {2, 4}};
adj[1] = {{0, 1}, {2, 2}};
adj[2] = {{0, 4}, {1, 2}};
vector<int> result = dijkstra(V, adj, 0);
cout << "Shortest distances:\n";
for (int d : result) {
cout << d << " ";
}
return 0;
}Python implementation
import heapq
def dijkstra(V, adj, S):
dist = [float('inf')] * V
dist[S] = 0
min_heap = [(0, S)]
while min_heap:
d, u = heapq.heappop(min_heap)
for v, wt in adj[u]:
if d + wt < dist[v]:
dist[v] = d + wt
heapq.heappush(min_heap, (dist[v], v))
return dist
adj_list = [
[[1, 1], [2, 4]],
[[0, 1], [2, 2]],
[[0, 4], [1, 2]]
]
print("Shortest distances:", dijkstra(3, adj_list, 0))Java implementation
import java.util.*;
public class DijkstraExample {
static class Pair {
int vertex, weight;
Pair(int v, int w) {
vertex = v;
weight = w;
}
}
public static int[] dijkstra(int V, List<List<Pair>> adj, int S) {
int[] dist = new int[V];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[S] = 0;
PriorityQueue<Pair> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a.weight));
pq.offer(new Pair(S, 0));
while (!pq.isEmpty()) {
Pair current = pq.poll();
for (Pair neighbor : adj.get(current.vertex)) {
if (current.weight + neighbor.weight < dist[neighbor.vertex]) {
dist[neighbor.vertex] = current.weight + neighbor.weight;
pq.offer(new Pair(neighbor.vertex, dist[neighbor.vertex]));
}
}
}
return dist;
}
public static void main(String[] args) {
int V = 3;
List<List<Pair>> adj = new ArrayList<>();
for (int i = 0; i < V; i++) adj.add(new ArrayList<>());
adj.get(0).add(new Pair(1, 1));
adj.get(0).add(new Pair(2, 4));
adj.get(1).add(new Pair(0, 1));
adj.get(1).add(new Pair(2, 2));
adj.get(2).add(new Pair(0, 4));
adj.get(2).add(new Pair(1, 2));
int[] result = dijkstra(V, adj, 0);
System.out.println("Shortest distances:");
for (int d : result) System.out.print(d + " ");
}
}JavaScript implementation
function dijkstra(V, adj, S) {
const dist = new Array(V).fill(Infinity);
dist[S] = 0;
const minHeap = [[0, S]];
while (minHeap.length > 0) {
minHeap.sort((a, b) => a[0] - b[0]);
const [d, u] = minHeap.shift();
for (const [v, wt] of adj[u]) {
if (d + wt < dist[v]) {
dist[v] = d + wt;
minHeap.push([dist[v], v]);
}
}
}
return dist;
}
const adjList = [
[[1, 1], [2, 4]],
[[0, 1], [2, 2]],
[[0, 4], [1, 2]]
];
console.log("Shortest distances:", dijkstra(3, adjList, 0));Go implementation
package main
import (
"container/heap"
"fmt"
"math"
)
type Edge struct {
to, weight int
}
type Item struct {
node, dist int
}
type PriorityQueue []Item
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool { return pq[i].dist < pq[j].dist }
func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
func (pq *PriorityQueue) Push(x interface{}) { *pq = append(*pq, x.(Item)) }
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}
func dijkstra(V int, adj [][]Edge, S int) []int {
dist := make([]int, V)
for i := 0; i < V; i++ {
dist[i] = math.MaxInt64
}
dist[S] = 0
pq := &PriorityQueue{}
heap.Init(pq)
heap.Push(pq, Item{S, 0})
for pq.Len() > 0 {
current := heap.Pop(pq).(Item)
for _, edge := range adj[current.node] {
if current.dist+edge.weight < dist[edge.to] {
dist[edge.to] = current.dist + edge.weight
heap.Push(pq, Item{edge.to, dist[edge.to]})
}
}
}
return dist
}
func main() {
adj := [][]Edge{
{{1, 1}, {2, 4}},
{{0, 1}, {2, 2}},
{{0, 4}, {1, 2}},
}
result := dijkstra(3, adj, 0)
fmt.Println("Shortest distances:", result)
}Rust implementation
use std::collections::BinaryHeap;
use std::cmp::Reverse;
fn dijkstra(v: usize, adj: &Vec<Vec<(usize, usize)>>, src: usize) -> Vec<usize> {
let mut dist = vec![usize::MAX; v];
let mut heap = BinaryHeap::new();
dist[src] = 0;
heap.push(Reverse((0, src)));
while let Some(Reverse((d, u))) = heap.pop() {
for &(vtx, wt) in &adj[u] {
if d + wt < dist[vtx] {
dist[vtx] = d + wt;
heap.push(Reverse((dist[vtx], vtx)));
}
}
}
dist
}
fn main() {
let adj = vec![
vec![(1, 1), (2, 4)],
vec![(0, 1), (2, 2)],
vec![(0, 4), (1, 2)],
];
let result = dijkstra(3, &adj, 0);
println!("Shortest distances: {:?}", result);
}Kotlin implementation
import java.util.*
data class Edge(val node: Int, val weight: Int)
def dijkstra(V: Int, adj: List<List<Edge>>, src: Int): IntArray {
val dist = IntArray(V) { Int.MAX_VALUE }
val pq = PriorityQueue(compareBy<Pair<Int, Int>> { it.first })
dist[src] = 0
pq.offer(Pair(0, src))
while (pq.isNotEmpty()) {
val (d, u) = pq.poll()
for (edge in adj[u]) {
if (d + edge.weight < dist[edge.node]) {
dist[edge.node] = d + edge.weight
pq.offer(Pair(dist[edge.node], edge.node))
}
}
}
return dist
}
fun main() {
val adj = listOf(
listOf(Edge(1, 1), Edge(2, 4)),
listOf(Edge(0, 1), Edge(2, 2)),
listOf(Edge(0, 4), Edge(1, 2))
)
val result = dijkstra(3, adj, 0)
println("Shortest distances: ${'$'}{result.joinToString(" ")}")
}Swift implementation
import Foundation
struct Edge {
let to: Int
let weight: Int
}
func dijkstra(V: Int, adj: [[Edge]], source: Int) -> [Int] {
var dist = Array(repeating: Int.max, count: V)
dist[source] = 0
var heap = [(0, source)]
while !heap.isEmpty {
heap.sort { $0.0 < $1.0 }
let (d, u) = heap.removeFirst()
for edge in adj[u] {
if d + edge.weight < dist[edge.to] {
dist[edge.to] = d + edge.weight
heap.append((dist[edge.to], edge.to))
}
}
}
return dist
}
let adj: [[Edge]] = [
[Edge(to: 1, weight: 1), Edge(to: 2, weight: 4)],
[Edge(to: 0, weight: 1), Edge(to: 2, weight: 2)],
[Edge(to: 0, weight: 4), Edge(to: 1, weight: 2)]
]
let result = dijkstra(V: 3, adj: adj, source: 0)
print("Shortest distances:", result)TypeScript implementation
function dijkstra(V: number, adj: number[][][], S: number): number[] {
const dist: number[] = new Array(V).fill(Infinity);
dist[S] = 0;
const minHeap: [number, number][] = [[0, S]];
while (minHeap.length > 0) {
minHeap.sort((a, b) => a[0] - b[0]);
const [d, u] = minHeap.shift()!;
for (const [v, wt] of adj[u]) {
if (d + wt < dist[v]) {
dist[v] = d + wt;
minHeap.push([dist[v], v]);
}
}
}
return dist;
}
const adjList: number[][][] = [
[[1, 1], [2, 4]],
[[0, 1], [2, 2]],
[[0, 4], [1, 2]]
];
console.log("Shortest distances:", dijkstra(3, adjList, 0));