ABSTRACT

While BFS finds the shortest path in unweighted graphs (least number of edges), it fails when edges have varying costs. Dijkstra’s Algorithm is a Greedy Algorithm that solves the Single-Source Shortest Path problem for weighted graphs, provided all edge weights are non-negative.

  • Category: Graph Traversal / Greedy Algorithm
  • Input: Directed graph with edge weights , source vertex
  • Output: dist(v) — the shortest total weight from to every vertex
  • Paradigm: Greedy, priority-queue-driven frontier expansion
  • Typical use cases: shortest path in weighted graphs (non-negative weights), routing, network cost minimization

Core Logic: Greedy Shortest Path

Why BFS Fails on Weighted Graphs

BFS assumes that every edge has a cost of . In a weighted graph, a path with more edges might actually have a lower total weight than a direct edge.

  • BFS Path: (Total Weight: 30)
  • Shortest Weighted Path: (Total Weight: )

Dijkstra’s Algorithm accounts for these costs by prioritizing paths with the smallest cumulative weight.

The Greedy Strategy

Dijkstra’s is a Greedy Algorithm. It makes the optimal choice at each step — picking the closest unvisited vertex — and assumes that this choice will lead to the overall shortest path.

Key Idea

  1. Assign a Distance of infinity to all nodes, except the start node (which is 0).
  2. Maintain a Priority Queue to store (distance, vertex) pairs.
  3. Always “relax” the neighbor: if the path to a neighbor through the current node is shorter than its previously known distance, update its distance and add it to the PQ.
  4. Once a node is “Done” (dequeued), its shortest path is guaranteed.

Pseudocode (Mid-Level Implementation)

High-Level Implementation

Dijkstra’s uses a Priority Queue to efficiently find the next vertex with the minimum distance.

Algorithm 29 Dijkstra's High Level

procedure Dijkstra's(G:directed graph with edgeweights,s:vertexG: \text{directed graph with edgeweights}, s: \text{vertex})

XX = empty, FF = {s}\{ s \}

Initialize dist(v)=dist(v) = \infty for all vv

dist(s)=0dist(s) = 0

while FF is not empty do

Pick vv in FF that has the lowest dist(v) value\underline{\text{lowest } dist(v) \text{ value}}

for each neighbor uu of vv do

if dist(u)>dist(v)+(v,u)dist(u) > dist(v) + \ell(v, u) then

move uu to FF

dist(u)=dist(v)+(v,u)dist(u) = dist(v) + \ell(v, u)

move vv from FF to XX

Variables & Data Structures

NameTypePurpose
XSetVertices whose shortest distance is finalized (“Done”)
FSet / Priority QueueFrontier — vertices discovered but not yet finalized, keyed by dist
distArray (vertex → number)Best known distance from to each vertex; starts at except
prevArray (vertex → vertex)Predecessor pointer, used to reconstruct the shortest path (low-level version)
ℓ(v, u)Edge weight functionThe cost of traveling directly from to

Helper Functions / Operations Used

  • “Relax” an edge — if , update and re-prioritize it in the queue
  • deletemin(H) — pop the vertex with the smallest current dist value
  • decreasekey(H, v) — lower the priority (distance key) of v in the priority queue after a successful relaxation

Pick in carefully Dijkstra's Algorithm falls into a problem where vertices may re-enter more than once. If we pick in carefully (always the minimum dist), we can avoid this — this is exactly what a priority queue gives us for free.

Optional Low-Level Implementation Full implementation using an explicit priority queue with deletemin / decreasekey:

Algorithm 30 Dijkstra

procedure Dijkstra(G,,sG, \ell, s)

for all uVu \in V do

dist(uu) :=:= \infty

prev(uu) :=:= null

dist(ss) :=0:= 0

HH := makequeue(VV)

while HH is not empty do

u:=u := deletemin(HH)

for all edges (u,v)E(u, v) \in E do

if dist(vv) > dist(uu) + (u,v)\ell(u, v) then

dist(vv) :=:= dist(uu) + (u,v)\ell(u, v)

prev(vv) :=:= uu

decreasekey(H,vH, v)


Proof of Correctness

Claim: Let be the length of the shortest path from to . Then after every iteration, for all vertices in .

This claim implies that once a vertex moves into , it will never move back to . Therefore, every vertex enters at most once.

Base Case: The first vertex to move into is .

Inductive Hypothesis: After vertices have been moved into , assume for all vertices in .

Inductive Step: Suppose is the next vertex to move into . Want to show .

Suppose by contradiction that , implying there exists a path such that . goes from to , so there is an edge that crosses the boundary of (with , ):

  • by the inductive hypothesis
  • by choice of (Dijkstra always picks the minimum-dist vertex in next)

Therefore:

\begin{align*} d(u) &= \underbrace{ len(P) }_{ s \to u } \geq \underbrace{ dist(w) + \ell(w,y) }_{ s \to y } \ &= dist(y) \geq dist(u) > d(u)\ &\therefore \boxed{d(u) > d(u)} \end{align*}

This is a contradiction, so the negation of our assumption must be true: .


Time & Space Complexity Analysis

General Case

Total runtime is:

Different implementations of Priority Queue have different trade-offs between the costs of deletemin and decreasekey. There isn't a single implementation that's optimal for all kinds of graphs.

Implementation-Dependent Variations

Array as a Priority Queue

Indexed by vertices, giving key value directly (e.g. Array[A] = 2, Array[B] = 9, …).

  • deletemin: — need to scan through the array to find which node contains the smallest distance
  • decreasekey: — array access by index means you can immediately find and update the (key, value) pair

Total Runtime:

Binary Heap as Priority Queue

Binary Heap A complete binary tree of objects (vertices) with the property that each key value of an object is less than or equal to the key value of its children.

  • Can be implemented with an array of vertices
  • The children of are and
  • The parent of is
  • deletemin: The minimum key is guaranteed to be the root. Removing it requires replacing the root with the last object and letting it trickle down where , so .
  • decreasekey: Decreasing a key may require the object to bubble up where , so .

How do we know where is in the binary heap? Keep a supplemental array (address book) indexed by , with pointers in both directions between this array and the binary heap elements.

Total Runtime:

Priority Queue Operations Overview

What it doesArray ImplementationHeap ImplementationNumber of Operations
insertAdd a new element with its priority value to the queue — just append to end — add to end, then bubble up
deleteminExtract the unvisited vertex with the smallest distance — must scan entire array to find minimum — remove root, move last to the top, bubble down
decreasekeyIf you find a shorter path to a vertex, update its distance in the queue — access by index — update value, bubble up

When to Use Which Implementation

Array Binary Heap
Sparse Graphs:
Dense Graphs:

Best / Worst / Average Case

  • Best / Worst / Average case: All the same order for a given PQ implementation — Dijkstra always processes every vertex once (deletemin) and considers every edge once (decreasekey/relaxation attempt), so there’s no early-exit case that changes the asymptotic bound.

Drawbacks / Constraints

  • Preconditions: Requires all edge weights to be non-negative.
  • The Negative Weight Problem: Dijkstra’s Algorithm does not work with negative edge weights.
    • The Reason: Dijkstra assumes that once a node is marked “Done,” no future path can possibly be shorter.
    • The Failure: A negative edge could “reduce” the cost of a path discovered later, breaking the greedy assumption.
  • Not suitable for: Graphs with negative edge weights — use Bellman-Ford Algorithm instead.
  • Alternatives to consider: Breadth First Search (BFS) if all edges have equal weight (simpler, same runtime without needing a priority queue).

Comparison of Shortest Path Algorithms

AlgorithmGraph TypeGuaranteed Shortest Path?
BFSUnweightedYes (by edge count)
DFSAnyNo
DijkstraWeighted (Positive only)Yes (by total weight)

References / Links