1. Uniform-Cost Search
Uniform-cost search (UCS) is an uninformed search algorithm that expands nodes in order of increasing path cost \(g(n)\) 1. It is the state-space generalisation of Dijkstra's shortest-path algorithm: where Dijkstra operates on an explicit graph in memory, UCS explores an implicitly defined state space, generating successors on demand. UCS is both complete (it will find a solution if one exists, given finite branching and positive edge costs) and optimal (the first solution found has the minimum path cost).
1.1 Problem Statement
Given a state-space search problem with:
- A start state \(s_{\text{start}}\)
- A goal test \(\text{GOAL}(s)\)
- A successor function \(\text{SUCC}(s)\) returning (state, action, cost) triples
- All step costs \(c > 0\)
find a path from \(s_{\text{start}}\) to any goal state that minimises the total path cost \(g\).
Variables
| Symbol | Meaning |
|---|---|
| \(s\) | State |
| \(g(n)\) | Path cost from start to node \(n\) |
| \(c(e)\) | Cost of edge \(e\) |
| \(b\) | Branching factor |
| \(d\) | Depth of shallowest optimal solution |
| \(C^*\) | Optimal solution cost |
| \(\varepsilon\) | Minimum positive step cost |
1.2 Algorithm
UCS maintains a priority queue (min-heap) keyed on \(g(n)\), and a visited set to avoid re-expanding states with a known-optimal cost.
UCS(start, goal_test, succ):
frontier = MinHeap() // keyed on g-cost
frontier.insert((0, start, [])) // (g, state, path)
visited = {} // state → best g seen
while frontier is not empty:
(g, s, path) = frontier.pop_min()
if s in visited: // already settled at lower cost
continue
visited[s] = g
if goal_test(s):
return path + [s], g // optimal path found
for (s', action, cost) in succ(s):
if s' not in visited:
frontier.insert((g + cost, s', path + [s]))
return FAILURE
Duplicate states in the frontier
UCS may contain multiple entries for the same state with different costs. The check if s in visited: continue handles this lazily: the first time a state is popped it has the optimal \(g\) (because the heap is sorted); later insertions are cheaper to ignore than to update in the heap.
1.3 Relation to Dijkstra's Algorithm
UCS and Dijkstra's algorithm are conceptually identical. The technical distinctions are engineering concerns:
| Aspect | Dijkstra | UCS |
|---|---|---|
| Graph representation | Explicit adjacency list / matrix in memory | Implicit; successors generated on demand |
| Goal | Shortest path to all vertices | Shortest path to one goal state |
| Visit termination | When all nodes are settled | When goal state is popped |
| Space | \(O(V + E)\) | \(O(b^d)\) frontier |
| Use case | Fixed graph, shortest-path queries | AI planning, game search, large/infinite graphs |
Both maintain the greedy settlement invariant: when a node is first popped from the priority queue, its \(g\)-value is optimal. The proof is the same in both cases — any alternative path would go through an unsettled node with at least as high a priority, contradicting it being shorter.
1.4 Completeness and Optimality
Completeness: If a solution exists and every step cost is \(\geq \varepsilon > 0\), then at depth \(d\) of the optimal solution the path cost is at most \(C^*\). UCS must expand all nodes with cost \(< C^*\) before reaching the goal; since each expansion increases cost by at least \(\varepsilon\), the number of expansions before the goal is finite. \(\square\)
Optimality: Suppose UCS returns a path of cost \(c' > C^*\). Then an optimal path of cost \(C^*\) exists. Its nodes were never all expanded — but since \(C^* < c'\), they would have lower priority and be expanded earlier. Contradiction. \(\square\)
1.5 Complexity
Let \(C^*\) be the optimal cost and \(\varepsilon\) the minimum step cost. In the worst case UCS explores all states whose cost \(\leq C^*\):
| Quantity | Bound |
|---|---|
| Time | \(O(b^{1 + \lfloor C^*/\varepsilon \rfloor})\) |
| Space | \(O(b^{1 + \lfloor C^*/\varepsilon \rfloor})\) |
When all step costs are equal (\(\varepsilon = 1\), equal costs), \(C^* = d \cdot 1\), so the bounds reduce to \(O(b^{d+1})\) — equal to BFS.
UCS can be dramatically more efficient than BFS when step costs vary and the optimal solution is at shallow depth with low cost.
1.6 Comparison with Related Algorithms
| Algorithm | Frontier order | Complete | Optimal | Time | Space |
|---|---|---|---|---|---|
| BFS | FIFO (depth) | ✓ (finite) | ✓ (unit costs) | \(O(b^d)\) | \(O(b^d)\) |
| DFS | LIFO | ✗ | ✗ | \(O(b^m)\) | \(O(bm)\) |
| UCS | Min \(g(n)\) | ✓ (\(\varepsilon > 0\)) | ✓ | \(O(b^{1+C^*/\varepsilon})\) | same |
| A* | Min \(g(n) + h(n)\) | ✓ (\(h\) admissible) | ✓ (\(h\) admissible) | depends on \(h\) | same |
| Dijkstra | Min \(g\) (explicit graph) | ✓ | ✓ | \(O((V+E)\log V)\) | \(O(V)\) |
UCS is equivalent to A* with \(h \equiv 0\). Adding an admissible heuristic \(h\) yields A*, which can skip large portions of the search space by using domain knowledge.
1.7 Interactive Demo
The canvas below runs UCS on a 9×9 grid with positive integer edge weights. The orange dashed frontier shows the current priority queue; settled cells display their \(g\)-cost. The path from (0,0) to (8,8) is highlighted in green on completion.
-
Stuart Russell and Peter Norvig. Artificial Intelligence: A Modern Approach. Pearson, 4th edition, 2020. ISBN 978-0-13-461099-3. ↩