Recursion
A problem-solving technique where a function calls itself to solve smaller instances of the same problem.
Key Concepts
Base Case: The simplest version of the problem where the function stops calling itself. Essential to prevent infinite loops / Stack Overflow.
Recursive Step: The part of the function where it calls itself with a smaller/simpler input, moving towards the base case.
Call Stack: Every recursive call pushes the current state onto the memory stack. Limits maximum depth (usually ~1000 in Python, varies by language).
Types of Recursion
Head/Pre Recursion: Operations are performed before the recursive call.
Tail/Post Recursion: The recursive call is the very last operation. Many compilers can optimize this to use $O(1)$ stack space (Tail Call Optimization).
Tree Recursion: The function makes multiple recursive calls (e.g., Fibonacci, Tree DFS). Time complexity is typically exponential unless memoized.
Optimizations & Variations
Memoization: Cache results of expensive recursive calls to avoid redundant work (Top-Down Dynamic Programming).
Backtracking: A form of recursion that builds a solution incrementally and abandons a path ("backtracks") as soon as it determines the path cannot lead to a valid solution.
Common SDE-3 Recursion Problems
Easy: Fibonacci Number, Factorial, Reverse Linked List, Same Tree.
Medium: Subsets, Permutations, Combinations, Validate Binary Search Tree.
Hard: Serialize and Deserialize Binary Tree, Word Search II, Regular Expression Matching.
Pattern Recognition
Tree / list structure → Natural recursion (process node, recurse on children or next). Base case: null/empty.
Choices at each step → Backtracking (make choice, recurse, undo). See backtracking.md.
Overlapping subproblems → Add memoization → top-down DP. See dynamic-programming/README.md.
Tail recursion → Can be converted to iterative loop (same O(1) space if TCO; otherwise iterative is safer for depth).
Interview Strategy
Identify: "Process tree/list" → recursion. "Generate all" / "find one valid" → backtracking. "Optimal + overlapping" → memo/DP.
Approach: Define base case first, then recursive step (smaller input). State time/space; mention stack depth limit. If converting to iterative, use explicit stack for tree.
Common mistakes: Missing base case; not making input strictly smaller (infinite recursion); forgetting to undo in backtracking.
Quick Revision
Base case: Must be reached; typically empty list, null node, or n=0/1. Recursive step: Call with smaller n or child/next.
Memoization: Cache by (state) before returning; check cache at start. Backtracking: path.append(x); recurse; path.pop().
Space: O(depth) for call stack. Tree DFS: O(h). List: O(n). Consider iterative + explicit stack if depth large.
See also: recursion/aditya-verma.md for IP/OP and decision-tree style.
Last updated