Recursion & Call Stack Frames
Quick Overview
Every recursive call pauses the current frame and stacks a new one holding its own variables. Without a base case the stack keeps growing until the engine refuses, at roughly 10,000 frames.
Deep Dive
1. Self-Referential Functions
Recursion is a problem-solving technique where a function solves a smaller sub-instance of the same problem by calling itself.
2. Anatomy of a Recursive Function
Every recursive function MUST have two parts:
- Base Case: A termination condition that stops recursion and returns a concrete result directly.
- Recursive Step: Reduces the problem size and calls the function again toward the base case.
function factorial(n) {
// Base Case
if (n <= 1) return 1;
// Recursive Step
return n * factorial(n - 1);
}
console.log(factorial(4)); // Output: 24 (4 * 3 * 2 * 1)
3. Visualizing Call Stack Frames
Each recursive call allocates a new stack frame in memory storing local variables:
[ factorial(1) -> returns 1 ] ◄── Base Case Reached (Stack Unwinds)
[ factorial(2) -> 2 * 1 ]
[ factorial(3) -> 3 * 2 ]
[ factorial(4) -> 4 * 6 ] ◄── Initial Invocation
4. Space Complexity of Recursion
Recursive functions consume O(D) auxiliary space on the call stack, where D is the maximum recursion depth.
5. Common Mistakes
- Missing or Incorrect Base Case: Calling recursion without a base case leads to infinite execution until the runtime throws
RangeError: Maximum call stack size exceeded(Stack Overflow!). - Redundant Recalculations: Naive recursive Fibonacci (
fib(n) = fib(n-1) + fib(n-2)) runs in horrificO(2^N)exponential time due to repeating identical subproblems. Use memoization or dynamic programming to reduce toO(N)!
6. Next Steps
Mastering recursion is required for trees and backtracking. Next, let's explore Backtracking Patterns!
Next Up in Data Structures & Algorithms Core Patterns
Big O Notation: Time & Space Complexity
Big O describes how work grows, not how long it takes. An `O(N^2)` algorithm on 1,000 items does a million steps; the same work at `O(N log N)` does about 10,000.
Dynamic Arrays & Amortized Resizing
A dynamic array doubles rather than growing by one, so the expensive copy lands on the 1st, 2nd, 4th and 8th push. Averaged across every push, that is `O(1)` amortised.
Two Pointers Pattern (Converging & Symmetrical)
A nested loop tests every pair in `O(N^2)`. On sorted data, moving two pointers inward from both ends rules out half the remaining candidates at each step and settles it in one `O(N)` pass.