notesonly.in

One notebook for every subject — open it anywhere.

Log in

Recursion and Backtracking Basics: Complete Beginner's Guide

Recursion is when a function solves a problem by calling itself on a smaller version of the same problem. To compute factorial(5) you compute 5 × factorial(4), then 4 × factorial(3), down to the base case factorial(0) = 1, which needs no further call. Backtracking is recursion with a superpower: it tries one path, and when that path fails it undoes the last choice and tries the next — like exploring a maze and retreating from dead ends.

These ideas matter because many problems are naturally self-similar: tree traversals, sorting, maze solving, and puzzles all split into smaller copies of themselves. Backtracking powers Sudoku solvers, the N-Queens puzzle, and route planning. Some problems are painful to solve any other way, so every programmer must learn to "think recursively".

Key Concepts

  • Base case: the smallest input handled directly, e.g., factorial(0) = 1. Without it, recursion never stops.
  • Recursive case: the step that shrinks the problem and calls the function again, e.g., factorial(n) = n × factorial(n − 1).
  • Call stack: each waiting call occupies a stack frame; depth-n recursion uses O(n) stack memory.
  • Stack overflow: the crash when recursion goes too deep and exhausts the call stack.
  • Backtracking: build a solution step by step; on hitting a dead end, undo the last choice and try the next one.
  • Pruning: rejecting doomed partial solutions early so whole dead subtrees are never explored.
  • Recurrence relation: the running-time equation, e.g., T(n) = T(n − 1) + O(1) = O(n) for factorial.

Formulas

  • Factorial: n! = n × (n−1)!, 0! = 1. Time O(n), stack space O(n).
  • Naive Fibonacci: F(n) = F(n−1) + F(n−2) gives ≈ O(1.618^n) — exponential, since subproblems are recomputed endlessly.
  • Recursive binary search: T(n) = T(n/2) + O(1) = O(log n), recursion depth O(log n).
  • Mergesort: T(n) = 2T(n/2) + O(n) = O(n log n).
  • Backtracking worst case: often O(b^d) for branching factor b and depth d — pruning is what keeps it practical.

Worked Example

Trace factorial(4): it calls factorial(3), which calls factorial(2), which calls factorial(1), which calls factorial(0). The base case returns 1; then the calls unwind: factorial(1) returns 1 × 1 = 1, factorial(2) returns 2 × 1 = 2, factorial(3) returns 3 × 2 = 6, factorial(4) returns 4 × 6 = 24. The stack grew 5 deep, then collapsed — 5 calls, 5 multiplications on the way back. Backtracking example: place 2 non-attacking queens on a 3×3 board. Try queen 1 at (0,0); then queen 2 at (1,0) is attacked vertically (reject), (1,1) diagonally (reject), (1,2) safe (place). Solution: (0,0) and (1,2). Had queen 1 gone to (0,1), every row-1 square would be attacked — backtrack, remove it, try the next column. Try, fail, undo, retry: that loop is backtracking.

Common Misconceptions

  • "Recursion is always slower than loops." Not meaningfully — both do O(n) work for factorial; recursion only adds O(n) stack space. Real waste comes from recomputed subproblems, like naive Fibonacci.
  • "Backtracking tries everything blindly." Wrong. Pruning rejects doomed partial solutions early; without it, backtracking would be pure brute force.
  • "Iterative versions are always better." Hand-simulating the call stack for tree traversals is far more error-prone than the natural recursive version.

Real-World Applications

  • Recursion: tree/graph traversals, quicksort and mergesort, expression parsing, fractals.
  • Backtracking: Sudoku solvers, N-Queens, maze pathfinding, regex engines, exam timetabling.
  • Combined: game AI exploring move trees recursively with backtracking, as in chess engines.

Recursion shrinks a problem until it becomes trivial; backtracking adds the courage to retreat from dead ends and try the next path. Always write the base case first — or your elegant recursion becomes an infinite one.