Uniform Cost Search (Dijkstra's Algorithm) Theory Guide
Try the Uniform Cost Search (Dijkstra's Algorithm) Solver →Think about booking a flight when you are on a strict budget. You don't care if the trip has three layovers, as long as it is the cheapest option available. Breadth-First Search (BFS) is the traveler who demands a direct flight, even if it costs a fortune. Uniform Cost Search (UCS) is the traveler who actually checks the price tags. It will gladly take a route with more stops if it means the final bill is smaller.
- It respects the actual cost of every step: In the real world, not all roads are equal. One path might be physically shorter but packed with traffic; another might be longer but completely clear. UCS doesn't just count the number of roads — it calculates the actual cost of traveling them.
- It expands patiently based on the past: At every step, UCS looks at all available paths and picks the one with the lowest total cost from the start, known as . It doesn't know where the goal is, so it explores outward fairly, like water spreading across a floor, always filling the cheapest gaps first.
- The guarantee is mathematical, not lucky: Because UCS always explores paths strictly in order from cheapest to most expensive, the exact moment it reaches the destination, it is impossible for a cheaper route to exist. Every cheaper option was already checked.
Uniform Cost Search is the foundational routing algorithm that real-world pathfinding is built on. Master this, and you will immediately understand the core mechanics behind every optimal search algorithm.
How to Trace UCS Using the Matrix Method
Set up your matrix before touching any numbers. Draw a table on your exam paper. Every node in the graph gets its own column across the top — these are your Target Nodes. The rows represent each step of the algorithm, and the leftmost cell of each row will hold the node you are currently evaluating. In the very first row, write the Start Node on the left. Give the Start Node's column a cost of . Fill every other column with — they are completely undiscovered and infinitely expensive for now.
Select the next Evaluating Node by scanning the latest row. Look at all the numbers sitting in your most recent row. Find the lowest number that has not already been locked in with a dash. That node becomes your Evaluating Node for the next row — write its name on the left side to begin a new row. If two nodes are tied with the exact same cost, break the tie alphabetically and pick the one that comes first in the alphabet.
Lock in the Evaluating Node immediately — it is permanently settled. The moment a node becomes your Evaluating Node, its shortest path is mathematically guaranteed. In the new row, place a dash () in that node's column. That dash means closed — you will never update, overwrite, or revisit this column again for the rest of the trace. A dashed column is dead.
Expand the Evaluating Node's neighbours and relax every edge. Look at every direct neighbour of your current Evaluating Node. For each neighbour, calculate the candidate cost: take the Evaluating Node's own locked-in cost and add the edge weight connecting them. Then compare this candidate cost to the number currently sitting in that neighbour's column. If your new cost is strictly lower, overwrite the old number — this is Edge Relaxation. If your new cost is equal or higher, do nothing and simply copy the old number straight down from the row above unchanged. For every non-neighbour column, also copy the number straight down — every cell in the new row must be filled.
Keep going row by row — only stop when the Goal node gets the dash. Repeat the process: scan the latest row for the lowest unlocked number, write that node as the new Evaluating Node, dash its column, and relax its neighbours. Do not stop the moment a cost appears in the Goal node's column — that only means it was discovered, not that it was finalized. The algorithm terminates only when the Goal node itself is selected as the Evaluating Node and receives its dash. The number it held in the row just before it was dashed is your optimal path cost.
The Cumulative Cost Function
Breaking Down the Formula & Edge Relaxation
- — The Running Total: This is the exact, verified cost paid to travel from the start node all the way to the current node along the best known path. There is no estimation here — is pure accumulated history. Every edge crossed had a real cost, and is the running total of all of them. It is the only number UCS uses to make every single decision.
- — The Edge Weight (The Price Tag): This is the cost of the single road connecting the parent node directly to node . It is given to you on the graph — just read it off the edge. You add this one edge cost to the parent's existing value to get the new candidate total for . Simple addition, but it is the entire engine of the algorithm.
- (Infinity) — The Undiscovered State: In your matrix, you fill empty columns with . This is not just a placeholder; it is the mathematical guarantee that the very first time you find a path to a new node, it will be accepted. Any real cost, no matter how high, is strictly less than .
- Edge Relaxation — The Overwrite Rule (Most Tested Concept): If UCS discovers a new path to a node it has already seen, it compares the new candidate cost against the that node currently holds in the matrix. If the new path is strictly cheaper, the algorithm relaxes the edge — you cross out the old, expensive cost and overwrite it with the new, cheaper value. If the new path is equal or more expensive, nothing changes. This step is what guarantees UCS settles on the true shortest path.
Solved Example: Tracing UCS on Paper
Draw this graph on your paper before reading the steps. Nodes: S (Start), A, B, G (Goal). Edges and their costs: S→A = 2, S→B = 5, A→B = 1, A→G = 10, B→G = 3. There are no heuristic values here — UCS does not use or . Now draw a matrix table with five columns: one unlabelled column on the far left for the Evaluating Node, then one column each for S, A, B, and G. Every cell in this table will hold exactly one thing: an accumulated cost number, a dash to show a locked node, or for undiscovered nodes.
Step 1: Initialize Row 1 with Start Node S
Write S on the left side of your first row — this is your starting Evaluating Node. Under the S column, write 0. It costs nothing to already be at the start. Under columns A, B, and G, write — these nodes are completely undiscovered and infinitely far away for now. Your first row reads: S(0), A(), B(), G(). S(0) is your only option and the smallest unlocked value, so the next row will evaluate S.
Step 2: Evaluate S — Discover Nodes A and B
Write S on the left of your second row to show it is being evaluated. S is now permanently locked — place a dash () under the S column. You will never touch that column again. Now look at S's direct neighbours: A and B. Calculate their costs: for A, and for B. Write 2 under column A and 5 under column B. G is not a direct neighbour of S, so simply copy its straight down from the row above. Your second row reads: S(), A(2), B(5), G(). The lowest unlocked value is A(2), so A becomes the next Evaluating Node.
Step 3: Evaluate A — Relax Node B and Spot the Trap
Write A on the left of your third row. A is now permanently locked — place a dash () under the A column. Now look at A's direct neighbours: G and B. For G: the cost through A is . Write 12 under column G. Stop — do not terminate just because G now has a real number. G is not locked yet, which means a cheaper route to it might still exist. For B: the cost through A is . Look up at B's current value in the row above — it holds a 5. Since , you found a cheaper path. Instead of copying the 5 straight down, write the new 3 under column B — this is Edge Relaxation. Your third row reads: S(), A(), B(3), G(12). The lowest unlocked value is B(3).
Step 4: Evaluate B — Find the Cheaper Route to G
Write B on the left of your fourth row. B is now permanently locked — place a dash () under the B column. Look at B's only open neighbour: G. The cost through B is . Look up at G's current value in the row above — it holds a 12. Since , you found a dramatically cheaper path to the goal. Overwrite G's value for this new row by writing 6 — another Edge Relaxation. Your fourth row reads: S(), A(), B(), G(6). The only remaining unlocked value is G(6).
Step 5: Lock in Goal Node G and Finalize the Path
Write G on the left of your fifth row. G is the only remaining unlocked node, and its cost is 6. Place a dash () under the G column to lock it in. The moment the Goal node receives its dash, the algorithm terminates immediately. The optimal path cost is 6. Tracing the relaxations backwards gives you the route: S → A → B → G. To confirm this is genuinely optimal: the path S → B → G costs , and S → A → G costs . UCS found the cheapest route not by luck, but by patiently relaxing edges until no cheaper alternative remained.
See the Interactive Solver in Action
Now that you can trace UCS by hand, use the solver to verify your work and watch the algorithm in motion. See the priority queue sort itself in real time, watch edge relaxations overwrite expensive costs with cheaper ones, and follow every update as the shortest path locks in step by step.
Your Turn to Practice
Trace a full solved exam question by hand, or build your own Uniform Cost Search (Dijkstra's Algorithm) question in the interactive solver.
Rules & Common Mistakes
- Exam Trap: Do Not Stop When a Number Appears in the Goal ColumnThe moment a real cost appears in the Goal node's column, students instinctively circle it and call the trace done. That is wrong every single time. A number in the Goal column only means a path to the goal was discovered — it does not mean that path is the shortest one. A cheaper route through a different node might still be sitting unlocked in the same row. Keep going row by row until the Goal node is explicitly chosen as the Evaluating Node and its column receives a dash (). That dash is the only valid termination signal.
- Exam Trap: Always Add to the Evaluating Node's Locked Cost — Not the Column ValueThis is the arithmetic mistake that silently corrupts an entire matrix. When calculating a neighbour's candidate cost, you must add the edge weight to the Evaluating Node's own locked cost — the number it held when it was selected and dashed. Never add the edge weight to whatever temporary number is currently sitting in the neighbour's column. That column value is a placeholder from a previous step. The formula is always: Evaluating Node's locked cost + edge weight = candidate cost for the neighbour.
- Exam Trap: Dashed Columns Are Permanently Dead — Never Update ThemThe moment a column receives a dash (), that node's shortest path is mathematically finalized. If you are expanding a node and notice that one of its neighbours already has a dash in the current row, ignore that neighbour completely. Do not calculate a new cost for it. Do not erase the dash and overwrite it with a number. A dashed column is closed forever — and erasing one to update a value is always wrong. It will cascade bad numbers through every remaining row and cost you most of your marks on that question.
- Pro Tip: Tied Numbers in the Matrix? Break Alphabetically and Keep MovingIf scanning a row reveals two unlocked columns with the exact same lowest cost, do not freeze. The algorithm will find the same optimal path cost regardless of which tied node you evaluate first — the final answer is guaranteed to be identical either way. To keep your trace consistent with standard marking rubrics, always break ties by picking the node whose letter comes first in the alphabet. Write that node on the left, dash its column, and continue. One second of decision, no marks lost.
Strengths, Weaknesses & When To Use It
When to use it:Reach for UCS whenever edge weights are different from each other and you need the guaranteed cheapest path — think varying flight prices, road distances with traffic delays, or network routing costs. If all the edge weights in your graph are identical, do not use UCS — just use Breadth-First Search, which gives you the same optimal answer with less overhead. And if your exam question provides heuristic values alongside the graph, do not use UCS either — upgrade to A*, which adds directional intelligence on top of UCS's cost-tracking and gets to the answer faster.
Advantages
- Optimal and Complete — No Negative Weights, No Problem: As long as every edge weight in the graph is zero or positive, UCS is mathematically guaranteed to find the absolute cheapest path to the goal every single time. It is also complete — if a path exists anywhere in the graph, UCS will find it. It does not guess, it does not approximate, and it does not cut corners. The first time the goal node receives its dash in the matrix, that cost is provably the lowest possible.
- It Actually Reads the Price Tag — Smarter Than BFS: Breadth-First Search treats every edge as if it costs the same and optimizes for the fewest hops. UCS knows that a 50-mile highway and a 1-mile alley are not the same thing. It tracks the real accumulated cost of every path and will happily take a route with more intermediate stops if the total bill is cheaper. On any graph where edge weights vary meaningfully, UCS will find a better answer than BFS every time.
Disadvantages
- Completely Blind — The Water Spill Effect: UCS has no , no compass, and no idea where the goal is. It expands outward from the start in every direction simultaneously, like water spilling across an uneven floor — always flowing into whatever is cheapest next, regardless of whether it is moving toward the goal or away from it. On a large map, this means UCS will spend enormous time exploring cheap paths in the completely wrong direction before it eventually reaches the destination. A* exists precisely to fix this blindness.
- Memory Explodes on Large Graphs: Because UCS explores in all directions without any directional filtering, it has to keep track of every single node it has ever generated to ensure it always picks the globally cheapest next step. On a small exam graph this is trivial. On a real-world map with millions of nodes, UCS does not slow down gracefully — it runs out of RAM. The more complex and sprawling the graph, the faster the memory footprint becomes unmanageable.
Uniform Cost Search vs. BFS vs. A*
Uniform Cost Search sits perfectly in the middle of the search algorithm family. It fixes the biggest blind spot of Breadth-First Search by actually reading edge weights — but it lacks the directional intelligence of A*, which uses a heuristic to aim directly at the goal. Think of UCS as the algorithm that learned to read price tags but still has no idea which way the destination is.
- The Price Tag (UCS vs. BFS): BFS counts hops and assumes every road costs the same. UCS reads the actual edge weights and tracks the real accumulated cost. On any graph where edge weights vary, BFS will likely hand you the wrong answer — UCS guarantees the right one.
- The Compass (UCS vs. A*): UCS has no — it is completely blind to where the goal is and spills outward in every direction chasing the cheapest accumulated cost. A* adds a heuristic that pulls the search toward the goal, finding the exact same shortest path while evaluating far fewer nodes. Same answer, dramatically less work.
- The Optimality Guarantee: UCS is guaranteed to find the cheapest path as long as no edge weight is negative. BFS is only optimal under one very specific condition: every single edge in the graph costs exactly the same. The moment edge weights differ, BFS loses its optimality guarantee entirely.
- What Your Exam Is Actually Testing: When an exam asks you to trace both BFS and UCS on the same graph, that graph was deliberately designed with a trap — a short path with massive edge weights and a longer path with tiny ones. They want to see if you know that BFS will blindly grab the fewest-hop route and walk straight into the expensive trap, while UCS patiently takes the cheaper detour and wins.
Implementation Pseudocode
// UCS Matrix Method — each iteration of the loop = one new row in your table
// columns = every node in the graph (e.g. S, A, B, C, G)
// cost[node] = the current best accumulated cost sitting in that node's column
// locked[node]= true when a node has been dashed — its path is permanently finalized
function ucsMatrix(allNodes, startNode, goalNode, edges):
// ── INITIALIZATION — ROW 1 ───────────────────────────────────────────
// Write 0 under the Start node column — it costs nothing to be there already
// Write Infinity under every other column — all other nodes are undiscovered
for each node in allNodes:
cost[node] = Infinity
locked[node] = false
cost[startNode] = 0
// ── MAIN LOOP — ONE ITERATION = ONE NEW MATRIX ROW ──────────────────
while true:
// ── SELECT — Find the lowest unlocked value in the current row ───
// Scan every column — ignore anything already dashed (locked)
// If two columns tie, break alphabetically
evaluatingNode = null
lowestCost = Infinity
for each node in allNodes:
if not locked[node] and cost[node] < lowestCost:
lowestCost = cost[node]
evaluatingNode = node
// No unlocked node found — graph is disconnected, no path exists
if evaluatingNode == null:
return failure
// ── TERMINATION TRAP — Only stop here, when the Goal is selected ─
// A cost appearing in the Goal column is NOT termination
// Termination only fires when the Goal becomes the Evaluating Node
if evaluatingNode == goalNode:
return cost[goalNode] // this is your optimal path cost
// ── LOCK — Place a dash in the Evaluating Node's column ──────────
// This node's shortest path is now mathematically guaranteed — never touch it again
locked[evaluatingNode] = true
// ── EXPAND AND RELAX — Fill in every column for the new row ─────
for each node in allNodes:
if locked[node]:
// This column already has a dash — skip it, never overwrite a locked column
continue
if node is a neighbour of evaluatingNode:
// Calculate candidate cost: always use the Evaluating Node's locked cost
candidateCost = cost[evaluatingNode] + edgeCost(evaluatingNode, node)
if candidateCost < cost[node]:
// EDGE RELAXATION — new path is cheaper, overwrite the column value
cost[node] = candidateCost
// else: existing value is equal or cheaper — copy it straight down, unchanged
else:
// Non-neighbour column — copy the value straight down from the previous row
// cost[node] is already correct — no action needed
// ── INITIAL CALL ─────────────────────────────────────────────────────
// ucsMatrix(allNodes, startNode, goalNode, edges)Time & Space Complexity
| Scenario | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Time Complexity | N/A | Here is the branching factor, is the cost of the optimal solution, and is the smallest edge weight. UCS is slower than BFS because it explores every path whose total cost is less than the optimal cost . The smaller the edge weights relative to the total path cost, the more nodes UCS has to expand before it can guarantee the cheapest route. | |
| Space Complexity | N/A | UCS must keep every generated node in memory to ensure it always picks the globally cheapest next step. It cannot discard a node just because it looks expensive right now, as a later expansion might reveal a cheaper path. Space grows at the same rate as time, meaning memory becomes the bottleneck long before computation time on large, sprawling graphs. | |
| Completeness & Optimality | Complete | Optimal | UCS is both complete and optimal — provided every edge weight is strictly positive. Complete means if a path to the goal exists, UCS will find it. Optimal means the path it finds is always the cheapest one possible. If an exam question asks for the condition of optimality, write this: all edge weights must be positive. |
Summary
Uniform Cost Search is the 'read the price tag' algorithm — it ignores hops, ignores shortcuts, and ignores visual proximity to the goal. The only thing it trusts is : the exact, verified cost of every path it has explored so far. That singular focus is what makes it mathematically guaranteed to find the cheapest route, every time, on any graph with positive edge weights. It is blind in a way that A* is not — it has no compass, no heuristic, and no sense of direction. But that blindness is also its integrity: because it evaluates every path purely on cost, it cannot be tricked by a graph designed to mislead. Master the matrix method, lock in the termination rule, and understand edge relaxation — and you will have a rock-solid command of the algorithm that sits at the core of Dijkstra's and powers every real-world routing system built on top of it.
UCS Exam Questions Students Always Get Wrong
I found the goal's cost in my matrix row, so I'm done, right?
Not even close — and this mistake costs marks every time. A number appearing in the Goal column only means a path to the goal was discovered, not that it is the cheapest one. A cheaper route through another node might still be unlocked and waiting. You are only done when the Goal node is selected as the Evaluating Node and its column receives a dash (). That dash is the only valid termination signal.
Why do I have to keep writing in the columns? Can't I just leave them blank?
Never leave a cell blank — it will cost you presentation marks and confuse your own trace. A blank cell implies the node does not exist or was never generated, which is a completely different statement from 'this node exists but has not been reached yet.' is the correct mathematical placeholder for an undiscovered node. Every cell in every row must hold exactly one thing: a cost, a dash, or .
Does it really matter which tied node I pick, or can I just choose either one?
Mathematically, both choices lead to the same final optimal cost — so the algorithm is not broken either way. The problem is practical: if your trace diverges from your instructor's solution key, you cannot verify your own work or pinpoint where you went wrong. Always break ties alphabetically to stay consistent with standard grading rubrics. One second of discipline saves you ten minutes of confusion.
I found a cheaper path to a node that already has a dash in its column. Should I update it?
Never. A dash means that node's shortest path is mathematically proven and permanently locked in. If you erase a dash and overwrite the value, you have broken the algorithm's core guarantee and every subsequent row will be wrong. UCS ensures that the first time a node is selected as the Evaluating Node, the path used to reach it is already optimal — so any 'cheaper' path you think you found is an arithmetic error, not a discovery.
Core University Curriculum
This algorithm and its manual calculation methods are foundational requirements in leading Computer Science and Software Engineering programs worldwide. You will find this topic heavily featured in the syllabi of these standard AI courses:
Explore Related Algorithms
Try the A* Algorithm Calculator
Watch how adding an educated heuristic guess turns UCS into A*, keeping the same optimal result while shrinking the search space dramatically.
Breadth-First Search Theory
See the algorithm UCS becomes when all edge weights are equal. Master the FIFO queue and see exactly when hop-counting fails you.