Common Mistakes & Tips
Common Mistakes & Tips
Section titled “Common Mistakes & Tips”Missing Base Case
Section titled “Missing Base Case”// ❌ WRONG — No base case → infinite recursionfunction factorial(n) { return n * factorial(n - 1); // What happens when n=0? Keeps going: -1, -2, -3...}
// ✅ CORRECTfunction factorial(n) { if (n <= 1) return 1; // ← BASE CASE return n * factorial(n - 1);}Infinite Recursion (Not Making Progress)
Section titled “Infinite Recursion (Not Making Progress)”// ❌ WRONG — n never changes!function countdown(n) { if (n === 0) return; console.log(n); countdown(n); // ← Should be countdown(n - 1)!}
// ❌ WRONG — wrong directionfunction sumUpTo(n) { if (n === 0) return 0; return n + sumUpTo(n + 1); // ← Going AWAY from base case!}Wrong State Handling in Backtracking
Section titled “Wrong State Handling in Backtracking”// ❌ WRONG — Forgetting to undo the choicefunction subsets(nums) { const result = []; function backtrack(index, current) { if (index === nums.length) { result.push([...current]); return; } current.push(nums[index]); backtrack(index + 1, current); // MISSING: current.pop() ← BUG! backtrack(index + 1, current); } backtrack(0, []); return result;}Storing Reference Instead of Copy
Section titled “Storing Reference Instead of Copy”// ❌ WRONG — All entries point to the SAME array!result.push(current);
// ✅ CORRECT — Store a COPYresult.push([...current]); // Spread operatorresult.push(current.slice()); // AlternativeOff-by-One in Index
Section titled “Off-by-One in Index”// ❌ WRONG — Starting from 0 generates duplicatesfor (let i = 0; i < nums.length; i++) { ... }
// ✅ CORRECT — Start from 'start' to maintain orderfor (let i = start; i < nums.length; i++) { backtrack(i + 1, current); // ← i+1, not start+1}Optimization Techniques
Section titled “Optimization Techniques”1. Memoization (Top-Down DP)
Section titled “1. Memoization (Top-Down DP)”function fibMemo(n, memo = new Map()) { if (memo.has(n)) return memo.get(n); if (n <= 1) return n;
const result = fibMemo(n - 1, memo) + fibMemo(n - 2, memo); memo.set(n, result); return result;}// O(n) vs O(2ⁿ) — dramatic improvement!2. Tail Recursion
Section titled “2. Tail Recursion”// Non-tail: O(n) stackfunction factorial(n) { if (n <= 1) return 1; return n * factorial(n - 1);}
// Tail: O(n) stack without TCO, but could be O(1) with TCOfunction factorialTail(n, acc = 1) { if (n <= 1) return acc; return factorialTail(n - 1, n * acc);}3. Effective Pruning
Section titled “3. Effective Pruning”// Sort first for better pruningcandidates.sort((a, b) => a - b);
// Then: if candidates[i] > remaining → break (not continue!)if (candidates[i] > remaining) break;4. Convert to Iteration When Appropriate
Section titled “4. Convert to Iteration When Appropriate”// Recursive factorial → Iterativefunction factorialIterative(n) { let result = 1; for (let i = 2; i <= n; i++) result *= i; return result;}
// Recursive Fibonacci → Bottom-up DPfunction fibIterative(n) { if (n <= 1) return n; let prev2 = 0, prev1 = 1; for (let i = 2; i <= n; i++) { [prev1, prev2] = [prev1 + prev2, prev1]; } return prev1;}// O(n) time, O(1) space — best possible!When to Convert Recursion to Iteration
Section titled “When to Convert Recursion to Iteration”| Convert to iteration when… | Keep as recursion when… |
|---|---|
| Simple linear recursion | Tree/graph traversal |
| Risk of stack overflow (n > 10000) | Problem is naturally recursive |
| Performance is critical | Backtracking problems |
| No TCO support | Code clarity matters |
Memoization vs Tabulation
Section titled “Memoization vs Tabulation”┌─────────────────────────────────────────────────────────┐│ ││ MEMOIZATION (Top-Down) TABULATION (Bottom-Up) ││ ││ Start with the big problem Start with base cases ││ Recursion + cache Iteration + table ││ Lazy: only computes needed Eager: computes all ││ May have stack overhead No recursion overhead ││ │└─────────────────────────────────────────────────────────┘Cheat Sheet
Section titled “Cheat Sheet”┌────────────────────────────────────────────────────────────────────┐│ RECURSION CHEAT SHEET ││ ││ EVERY recursive function needs: ││ 1. BASE CASE (when to stop) ││ 2. RECURSIVE CASE (call self with smaller input) ││ 3. PROGRESS toward base case ││ ││ BACKTRACKING template: ││ for each choice: ││ if valid: ││ MAKE choice ││ RECURSE ││ UNDO choice ← THE KEY STEP ││ ││ COMMON PATTERNS: ││ Pick/Not-Pick → Subsets, Subsequences ││ Loop + Recurse → Permutations, Combinations ││ Grid DFS → Word Search, Maze, Islands ││ Constraint Check → N-Queens, Sudoku ││ ││ COMPLEXITY: ││ Subsets: O(2ⁿ) Permutations: O(n!) ││ Combination: O(2ᵗ) N-Queens: O(n!) ││ ││ REMEMBER: ││ ✓ Always store COPIES in results: [...current] ││ ✓ Always UNDO after recursive call ││ ✓ Trust the recursion — think about ONE level ││ ✓ Draw the decision tree for clarity ││ │└────────────────────────────────────────────────────────────────────┘Next: Real-World Applications →