E ExamMaster

Artificial Intelligence · AI Foundations

Depth-First Search and Iterative Deepening

In AI because depth-first exploration — and the iterative-deepening repair of its weaknesses — is the memory-light workhorse behind constraint solvers, planners and game-tree…

Depth-first search makes the opposite bet to BFS: follow one path as deep as it goes and retreat only at dead ends, holding just the current path in memory instead of an entire level. This lesson takes the bet seriously — what it saves, the two ways it fails, why its shape is exactly right for constraint solvers and game trees, and how iterative deepening buys BFS's guarantees back at DFS prices.

  • Artificial Intelligence
  • Medium level
  • 6 concepts

1Depth-first search

Depth-first search expands the newest frontier node first — a LIFO stack — so it commits to one branch and goes one level deeper with every step, retreating only when the branch runs out of moves.

The memory bill is the whole attraction. DFS holds the current path plus the untried siblings hanging off it: with branching factor b and maximum depth m, that is O(b \cdot m) nodes against BFS's O(b^d) — a corridor instead of a wave. A path 100 deep at branching 10 is about a thousand held nodes, where a BFS level at that depth would hold more states than there are atoms to build the RAM from.

You have written DFS even if you never named it: any recursive traversal is depth-first, with the language's call stack as the frontier. That is also why deep recursion overflows — the stack is the memory bill made physical.

What DFS refuses to promise: the goal may sit two moves from the start while the search is forty deep in the wrong subtree, so the first path found can be needlessly long — finding a solution fast and finding a short solution are different jobs.

Animation: an eight-node tree. DFS dives down one path S-A-C-H, marking waiting siblings as frontier; H is crossed out as a dead end, the dived edges dim one by one while backtracking, D is tried and crossed out too, then the search dives S-B and finds the goal E last. The final frame highlights the found path, captioned goal found last, depth first, low memory, only one path and its siblings are held
DFS commits to one path and only retreats at a dead end, so it holds little memory - but it can wander deep first and makes no shortest-path promise.

One cycle of the dive

  1. DiveExpand the newest node and push its children; the search goes one level deeper each step.
  2. Dead endThe current branch has no untried moves left and none of them was the goal.
  3. BacktrackPop back to the most recent node with an untried sibling and dive again from there.

Coding lab. DFS visit order, by hand and by networkx runs in the app, with checks on your output.

Given that DFS can return a needlessly long path and loop on cycles, why is it still worth having?
  1. It only has to remember the path it is currently on, so its memory grows with depth rather than with the frontier
  2. It reaches the goal faster than BFS on every graph
  3. It never revisits a state, so it needs no explored set
  4. It becomes optimal as soon as the graph has no cycles

Memory is the whole argument. BFS holds an entire level at once, which is what makes DFS the survivable option on a deep graph.

2The two ways DFS fails

DFS's failures are not edge cases to memorise; both follow directly from expanding newest-first, and both decide real system designs.

Failure one: it may never come back. On an infinite state space — and generated spaces are routinely infinite, since 'append one more token' or 'place one more piece' always offers a next move — DFS can dive down one endless branch while a goal sits two moves from the start on another. On a finite map with cycles, the plain tree-search variant recirculates the loop forever instead. DFS is incomplete without a guard.

Failure two: the answer may be poor. Even when it terminates, the first goal found is whatever the dive hit first, not the nearest — DFS is not optimal under any cost model.

The standard BFS remedy is the wrong medicine here. A full explored set does make DFS terminate on finite graphs, but storing every visited state erases the O(b \cdot m) memory profile that justified DFS in the first place. The lightweight guard is to check for repeats along the current path only — that kills cycles for a memory cost of the path length, and leaves the deeper repair to depth limits.

Failure, cause, guard
FailureRoot causeThe guard
Dives forever on an infinite branchThere is always a newest child to expandImpose a depth limit
Loops forever on cyclesTree search never recognises a repeated stateCheck the current path for repeats
Returns a needlessly long pathThe first goal hit is not the shallowestRerun under iterative deepening

3Backtracking: DFS as a solver

Backtracking search is DFS wearing work clothes: the states are partial solutions, a move fills in one more piece, and a dead end is a partial solution that already violates a constraint.

Sudoku, n-queens, timetabling, SAT — the industrial constraint solvers all run this shape at their core. DFS's memory profile is exactly right here, because the current path is the current partial assignment, and retreating one level is simply undoing the last choice.

The move that turns it from brute force into an algorithm is pruning: test the constraints the moment a piece is placed, and the search never explores any completion of a doomed prefix — the earlier the cut, the larger the exponential subtree that dies with it.

This is also the first place search and learning meet as colleagues rather than rivals: modern SAT solvers learn which branch to try first from the run so far, but the correctness of the final answer still comes from the exhaustive backtracking skeleton.

Figure. The assignment x = 1 violates a constraint the moment it is placed, so its whole subtree (dashed) is never generated; the search retreats and completes the solution under x = 2. Checking at placement, not at the end, is what lets one comparison discard every completion of the doomed prefix.

The solver cycle

  1. AssignExtend the partial solution by one choice — one queen placed, one cell filled.
  2. CheckTest the constraints touching that choice immediately, not after the grid is full.
  3. UndoOn a violation, retract the choice and try its next alternative; retreat further only when the alternatives run out.

4Depth-limited search

Depth-limited search is DFS with a contract: dive at most \ell moves deep, treat depth-\ell nodes as if they had no children, and report honestly.

Honesty needs three answers, not two. 'Found' and 'no solution anywhere' are the usual pair; the third is 'cutoff' — the limit was hit somewhere, so a solution deeper than \ell may still exist. Collapsing cutoff into failure is a real API bug: it turns 'I did not look further' into 'there is nothing there'.

Choose \ell from problem knowledge when you have it: if every solvable instance is solvable within some known diameter, that diameter is a safe limit. With \ell at or above the goal depth the search is complete; below it, it cannot succeed at all — and it is never optimal, since inside the limit it is still plain DFS.

Alone, depth-limited search looks like a compromise nobody asked for — and alone, it is. Its real role is to be the inner loop that iterative deepening calls with \ell = 0, 1, 2, \dots

What the limit does to the promises
ConditionComplete?Optimal?
Limit at or above the goal depthYes — every state within the limit is reachedNo — inside the limit it is still DFS
Limit below the goal depthNo — every dive is cut off before a goalNo
Any limit, with the current path checked for repeatsTerminates always — finitely many nodes fit within the limitNo

5Iterative deepening: BFS answers at DFS prices

Iterative deepening runs depth-limited search with \ell = 0, then 1, then 2, and stops at the first limit that finds a goal. Each individual run is DFS-shaped, so memory stays O(b \cdot d); the limits sweep shallowest-first, so the first solution found is a shallowest one — completeness and unit-cost optimality, the whole BFS contract, at corridor memory.

The objection everyone raises — it re-explores everything, every iteration — dissolves under arithmetic. The level at depth k is regenerated once per iteration from k onwards, so shallow levels are redone many times; but shallow levels are geometrically tiny, and the deepest level, which dominates the total, is generated exactly once.

At branching factor 10 and goal depth 5, all the repetition together costs about 11% extra generation over a single BFS — the worked ledger below has the exact count. That price buys BFS's guarantees with roughly fifty held nodes instead of a hundred thousand.

This is why iterative deepening is the default recommendation for uninformed search when the goal depth is unknown and the space is large. Game engines run the same idea against a clock: deepen until time runs out, and the deepest completed iteration is always a valid answer.

Figure. All of iterative deepening's re-exploration, summed over its six iterations at branching factor 10 and depth 5, is the sliver between the bars: about 11% extra generation. What the figure cannot show is the memory column — about 50 held nodes against 100,000 — which is the entire point of paying the sliver.

The price of repeating yourself

Branching factor b = 10, shallowest goal at depth d = 5. Count node generations: BFS generates each level once; iterative deepening regenerates the level at depth k in every iteration from k to 5 — that is 6 − k times.

  • BFS total = 1 + 10 + 100 + 1,000 + 10,000 + 100,000111,111 nodes
  • IDDFS weights each level: 6×1 + 5×10 + 4×100 + 3×1,000 + 2×10,000 + 1×100,000123,456 nodes
  • Overhead = 123,456 / 111,111≈ 1.11 — about 11% extra
  • Memory held meanwhile: 10 × 5 for the path and siblings, against BFS's last level50 nodes vs 100,000

Pro tip. The digits 123,456 are a coincidence of b = 10, but the lesson is general: for any branching factor above 1 the repetition costs a constant factor of about b/(b−1) — never another exponential.

Coding lab. Implement iterative deepening runs in the app, with checks on your output.

6Where the DFS family runs in production

The DFS shape may be the most-executed algorithm skeleton in computing, because 'process this thing, then recurse into what it points to' is depth-first search.

Compilers and build tools run it as topological sort — visit a module's dependencies before the module — and the same traversal detects the circular import. Filesystem walks are DFS with directories as nodes. Game engines run their minimax lookahead depth-first to a limit, because the tree is far too wide for one breadth level to fit in memory. SAT and constraint solvers are backtracking, as the solver concept showed.

The selection rule an engineer actually uses: BFS when the answer is about distance from the start; DFS when the whole subtree must finish before its parent, or when memory rules out a level-wide wave; iterative deepening when you want shortest-answer guarantees on a bottomless space or against a clock.

The family in the wild
SystemThe DFS trait it uses
Build systems, module loadersTopological order — dependencies finish before dependents, and a back-edge is a circular import
Filesystem toolsRecurse into a directory before its siblings; the call stack is the frontier
Game-tree lookaheadDepth-first to a limit — a full breadth level would never fit in memory
SAT / constraint solversBacktracking over partial assignments, pruning doomed prefixes early

Notes

  • DFS commits to one path and retreats at dead ends; memory grows with depth, not with the frontier.
  • DFS makes no shortest-path promise and can dive forever on infinite or cyclic spaces.
  • Iterative deepening reruns depth-limited DFS with growing limits, recovering BFS's guarantees at DFS's memory for a small constant overhead.

Exam traps & shortcuts

  • A recursive traversal is DFS whether you called it that or not — the call stack is the frontier, and a stack overflow is the depth bill arriving.
  • Adding a full explored set to DFS destroys the memory advantage that justified choosing it; on big graphs, check for repeats along the current path instead.
  • When someone asks for BFS answers under DFS memory, the answer is iterative deepening — the repeated shallow work is a small constant factor, because the deepest level dominates.

Recap

These four points feed the cost-aware lesson.

The bet
Expand newest-first: memory is the current path and its siblings, and the price is every promise about path quality.
The failures
Infinite or cyclic spaces defeat plain DFS, and its first answer is not the shortest — guard the current path, and limit the depth.
Backtracking
Solvers are DFS over partial assignments: check constraints at each placement and prune doomed prefixes early.
Iterative deepening
Sweep depth limits upward: BFS's completeness and shortest-answer guarantee at DFS memory, for a small constant overhead.

Practise Depth-First Search and Iterative Deepening

Reading is free and needs no account. Practice, mocks and progress live in the app.

  • 1 quick check with worked explanations
  • Timed mocks scored with the real marking scheme
  • Readiness tracked per topic, kept on your device
Continue with Google — freeNo card, no trial. Works offline once installed.