Recursion Pattern
🎯 Explain Like I'm 5...
Imagine you have a big task: clean your entire toy room! 🧸
🧒 Kid's Way (No Recursion): Try to clean everything at once - you get confused and don't know where to start! 😵
🧙 Smart Way (Recursion): Break it down into smaller tasks:
- • Clean one toy →
- • Now you have a SMALLER room to clean
- • Keep doing this until room is clean! 🎉
🚀 Why is it powerful? Recursion lets you solve BIG problems by solving SMALL versions of the same problem! It's like using a magic trick where you make the problem smaller each time until it's easy to solve. Think of it like a Russian nesting doll - you open one doll to find a smaller one inside!
🎨 See It In Action!
Below you'll find interactive animations for each example. Use the controls to play, pause, and step through each algorithm!
When Should You Use This Pattern?
- ✓You're working with trees or graphs (naturally recursive structures)
- ✓The problem can be divided into smaller identical subproblems
- ✓Mathematical problems with self-similar patterns
- ✓You need to generate all possibilities
📝 Example 1: Calculate Factorial
Factorial of n (written as n!) means: n × (n-1) × (n-2) × ... × 1. For example, 5! = 5 × 4 × 3 × 2 × 1 = 120
Step-by-Step Thinking:
- Base case: If n is 0 or 1, return 1 (stopping condition!)
- Recursive case: n! = n × (n-1)!
- Each call makes the problem smaller: 5! → 4! → 3! → 2! → 1!
- Then multiply results back up: 1 → 2 → 6 → 24 → 120 ✓
Factorial(5) - Recursive Call Stack
public class Recursion { // Calculate factorial using recursion // Example: factorial(5) = 5 * 4 * 3 * 2 * 1 = 120 public static int factorial(int n) { // Base case: factorial of 0 or 1 is 1 if (n <= 1) { return 1; } // Recursive case: n! = n * (n-1)! return n * factorial(n - 1); } public static void main(String[] args) { System.out.println("Factorial of 5: " + factorial(5)); // 120 System.out.println("Factorial of 0: " + factorial(0)); // 1 System.out.println("Factorial of 7: " + factorial(7)); // 5040 }}/* * How it works for factorial(5): * factorial(5) = 5 * factorial(4) * factorial(4) = 4 * factorial(3) * factorial(3) = 3 * factorial(2) * factorial(2) = 2 * factorial(1) * factorial(1) = 1 (base case!) * * Then multiply back up: * factorial(2) = 2 * 1 = 2 * factorial(3) = 3 * 2 = 6 * factorial(4) = 4 * 6 = 24 * factorial(5) = 5 * 24 = 120 */📝 Example 2: Fibonacci Sequence
The Fibonacci sequence is: 0, 1, 1, 2, 3, 5, 8, 13... Each number is the sum of the previous two!
Step-by-Step Thinking:
- Base cases: fib(0) = 0, fib(1) = 1
- Recursive case: fib(n) = fib(n-1) + fib(n-2)
- To find fib(5), we need fib(4) and fib(3)
- This creates a tree of recursive calls that eventually reaches base cases
Fibonacci(5) - Recursion Tree
public class Fibonacci { // Calculate nth Fibonacci number using recursion // Sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21... public static int fibonacci(int n) { // Base cases if (n == 0) return 0; if (n == 1) return 1; // Recursive case: fib(n) = fib(n-1) + fib(n-2) return fibonacci(n - 1) + fibonacci(n - 2); } // Optimized version with memoization public static int fibonacciMemo(int n, int[] memo) { if (n == 0) return 0; if (n == 1) return 1; // Check if already calculated if (memo[n] != -1) { return memo[n]; } // Calculate and store in memo memo[n] = fibonacciMemo(n - 1, memo) + fibonacciMemo(n - 2, memo); return memo[n]; } public static void main(String[] args) { System.out.println("Fibonacci of 6: " + fibonacci(6)); // 8 System.out.println("Fibonacci of 10: " + fibonacci(10)); // 55 // Using memoization for better performance int n = 10; int[] memo = new int[n + 1]; for (int i = 0; i <= n; i++) memo[i] = -1; System.out.println("Fibonacci of 10 (memo): " + fibonacciMemo(10, memo)); // 55 }}/* * Recursion tree for fibonacci(5): * fib(5) * / \ * fib(4) fib(3) * / \ / \ * fib(3) fib(2) fib(2) fib(1) * / \ / \ / \ * fib(2) fib(1) f(1) f(0) f(1) f(0) * / \ * f(1) f(0) * * Notice: Without memoization, we calculate fib(3) twice, fib(2) three times! * Time complexity: O(2^n) without memo, O(n) with memo */📝 Example 3: Sum of Array Elements
Find the sum of all numbers in an array: [1, 2, 3, 4, 5]
Step-by-Step Thinking:
- Base case: Empty array returns 0
- Recursive case: first element + sum(rest of array)
- sum([1,2,3,4,5]) = 1 + sum([2,3,4,5])
- = 1 + 2 + sum([3,4,5]) = 1 + 2 + 3 + sum([4,5]) = ... = 15 ✓
public class ArraySum { // Sum all elements in array using recursion public static int sumArray(int[] arr, int index) { // Base case: reached end of array if (index >= arr.length) { return 0; } // Recursive case: current element + sum of rest return arr[index] + sumArray(arr, index + 1); } // Alternative: working backwards from the end public static int sumArrayBackwards(int[] arr, int index) { // Base case: before start of array if (index < 0) { return 0; } // Recursive case return arr[index] + sumArrayBackwards(arr, index - 1); } public static void main(String[] args) { int[] numbers = {1, 2, 3, 4, 5}; System.out.println("Sum (forwards): " + sumArray(numbers, 0)); // 15 System.out.println("Sum (backwards): " + sumArrayBackwards(numbers, numbers.length - 1)); // 15 }}/* * How sumArray works for [1, 2, 3, 4, 5]: * sumArray([1,2,3,4,5], 0) = 1 + sumArray([...], 1) * sumArray([1,2,3,4,5], 1) = 2 + sumArray([...], 2) * sumArray([1,2,3,4,5], 2) = 3 + sumArray([...], 3) * sumArray([1,2,3,4,5], 3) = 4 + sumArray([...], 4) * sumArray([1,2,3,4,5], 4) = 5 + sumArray([...], 5) * sumArray([1,2,3,4,5], 5) = 0 (base case) * * Then add back up: 5 + 4 + 3 + 2 + 1 = 15 */public class Power { // Calculate x^n using recursion public static int power(int x, int n) { // Base case: anything to power 0 is 1 if (n == 0) { return 1; } // Recursive case: x^n = x * x^(n-1) return x * power(x, n - 1); } // Optimized version using divide and conquer // x^n = (x^(n/2))^2 if n is even // x^n = x * (x^(n/2))^2 if n is odd public static int powerOptimized(int x, int n) { // Base case if (n == 0) return 1; // Calculate half power int halfPower = powerOptimized(x, n / 2); // If n is even: x^n = (x^(n/2))^2 if (n % 2 == 0) { return halfPower * halfPower; } // If n is odd: x^n = x * (x^(n/2))^2 else { return x * halfPower * halfPower; } } public static void main(String[] args) { System.out.println("2^5 = " + power(2, 5)); // 32 System.out.println("3^4 = " + power(3, 4)); // 81 System.out.println("5^0 = " + power(5, 0)); // 1 System.out.println("2^10 (optimized) = " + powerOptimized(2, 10)); // 1024 }}/* * Regular power(2, 5): * power(2, 5) = 2 * power(2, 4) * power(2, 4) = 2 * power(2, 3) * power(2, 3) = 2 * power(2, 2) * power(2, 2) = 2 * power(2, 1) * power(2, 1) = 2 * power(2, 0) * power(2, 0) = 1 * Result: 2 * 2 * 2 * 2 * 2 = 32 * Time: O(n) * * Optimized powerOptimized(2, 5): * power(2, 5) = 2 * power(2, 2)^2 * power(2, 2) = power(2, 1)^2 * power(2, 1) = 2 * power(2, 0)^2 * power(2, 0) = 1 * Time: O(log n) - much faster! */🔑 Key Points to Remember
- →Every recursion needs a BASE CASE (stopping condition) or it will run forever!
- →Each recursive call should make the problem SMALLER
- →Recursion uses the call stack - each call waits for the next to finish
- →Can be more elegant than loops, but may use more memory (stack space)
- →Time complexity varies: O(n) for linear, O(2^n) for tree-like recursion
💡 Pro Tips
- 💡Always identify the base case first - that's your exit strategy!
- 💡Draw the recursion tree to understand how calls branch and merge
- 💡Consider using memoization to avoid recalculating the same values
- 💡Some recursive solutions can be converted to iterative (using loops) for better memory efficiency
💪 Practice Problems
- •Power of a Number (x^n)
- •Reverse a String
- •Binary Tree Traversals (Preorder, Inorder, Postorder)
- •Tower of Hanoi
- •Generate All Subsets
- •Sum of Digits