← Back to All Patterns

Fibonacci Numbers (Dynamic Programming)

📊 Fibonacci Numbers - Dynamic Programming

DP Array (Building F(5)):

0
F(0)

Space-Optimized (O(1) space):

prev2
0
+
prev1
0
=
current
0

Step 1: Base case: F(0) = 0

Progress: 1 / 6

Speed:

Pattern:

Each Fibonacci number = sum of previous two. DP stores results to avoid recomputation. Space-optimized version only keeps last 2 values!

🎯 Explain Like I'm 5...

Imagine climbing stairs 🪜 but you're special - you can take 1 step OR 2 steps at a time! How many ways can you reach the top?

🪜 The Magic Stairs Pattern:

  • Stair 0: 1 way (don't move!)
  • Stair 1: 1 way (take 1 step)
  • Stair 2: 2 ways (1+1 steps OR take one big 2-step!)
  • Stair 3: 3 ways (1+1+1 OR 1+2 OR 2+1)
  • Stair 4: 5 ways (1+1+1+1 OR 1+1+2 OR 1+2+1 OR 2+1+1 OR 2+2)

🔍 Notice the pattern: 1, 1, 2, 3, 5... Each number is the sum of previous TWO numbers!

Ways to reach step 4 = (ways to reach step 3) + (ways to reach step 2) = 3 + 2 = 5 ✨

🚀 The Secret: You can get to ANY step from the previous step (1 step back) OR from 2 steps back! So just ADD those two numbers! It's like each number is made from combining its two neighbors! 🧮

When Should You Use This Pattern?

  • Answer depends on previous answers Counting ways
  • to reach a state
  • Staircase, house robber style problems
  • Can break problem into overlapping subproblems

📝 Example 1: Fibonacci Number

Calculate the nth Fibonacci number efficiently.

Evolution of Solutions:

  1. Naive recursion: O(2^n) - very slow!
  2. Memoization (top-down DP): O(n) time, O(n) space
  3. Tabulation (bottom-up DP): O(n) time, O(n) space
  4. Space optimized: O(n) time, O(1) space!
Fibonacci.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import java.util.*;
public class Fibonacci {
// Approach 1: Naive recursion (SLOW - O(2^n))
public static int fibRecursive(int n) {
if (n <= 1) return n;
return fibRecursive(n - 1) + fibRecursive(n - 2);
}
// Approach 2: Memoization (Top-down DP)
public static int fibMemo(int n) {
Map<Integer, Integer> memo = new HashMap<>();
return fibMemoHelper(n, memo);
}
private static int fibMemoHelper(int n, Map<Integer, Integer> memo) {
if (n <= 1) return n;
if (memo.containsKey(n)) return memo.get(n);
int result = fibMemoHelper(n - 1, memo) + fibMemoHelper(n - 2, memo);
memo.put(n, result);
return result;
}
// Approach 3: Tabulation (Bottom-up DP)
public static int fibTabulation(int n) {
if (n <= 1) return n;
int[] dp = new int[n + 1];
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// Approach 4: Space optimized (Best!)
public static int fibOptimized(int n) {
if (n <= 1) return n;
int prev2 = 0;
int prev1 = 1;
int current = 0;
for (int i = 2; i <= n; i++) {
current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return current;
}
public static void main(String[] args) {
int n = 10;
System.out.println("Fib(" + n + ") recursive: " + fibRecursive(n));
System.out.println("Fib(" + n + ") memo: " + fibMemo(n));
System.out.println("Fib(" + n + ") tabulation: " + fibTabulation(n));
System.out.println("Fib(" + n + ") optimized: " + fibOptimized(n));
// All output: 55
}
}

📝 Example 2: Climbing Stairs

Count the number of ways to climb n stairs if you can take 1 or 2 steps at a time.

ClimbingStairs.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class ClimbingStairs {
// This is exactly Fibonacci!
// To reach step n, you came from step n-1 or n-2
// ways(n) = ways(n-1) + ways(n-2)
public static int climbStairs(int n) {
if (n <= 2) return n;
int prev2 = 1; // ways to reach step 1
int prev1 = 2; // ways to reach step 2
int current = 0;
for (int i = 3; i <= n; i++) {
current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return current;
}
// With k steps allowed (more general)
public static int climbStairsK(int n, int k) {
int[] dp = new int[n + 1];
dp[0] = 1; // One way to stay at ground
for (int i = 1; i <= n; i++) {
// Sum all possible previous steps (1 to k steps back)
for (int step = 1; step <= k && step <= i; step++) {
dp[i] += dp[i - step];
}
}
return dp[n];
}
public static void main(String[] args) {
int n = 5;
System.out.println("Ways to climb " + n + " stairs: " + climbStairs(n));
// Output: 8
// [1+1+1+1+1, 1+1+1+2, 1+1+2+1, 1+2+1+1, 2+1+1+1, 1+2+2, 2+1+2, 2+2+1]
System.out.println("Ways with 3 steps allowed: " + climbStairsK(n, 3));
// Output: 13
}
}

📝 Example 3: House Robber

Rob houses to maximize money, but can't rob two adjacent houses.

HouseRobber.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class HouseRobber {
// At each house: rob it (skip previous) or skip it (keep previous max)
// dp[i] = max(nums[i] + dp[i-2], dp[i-1])
public static int rob(int[] nums) {
if (nums.length == 0) return 0;
if (nums.length == 1) return nums[0];
int prev2 = 0;
int prev1 = nums[0];
int current = 0;
for (int i = 1; i < nums.length; i++) {
// Rob current house + max from 2 houses back
// OR skip current, keep previous max
current = Math.max(nums[i] + prev2, prev1);
prev2 = prev1;
prev1 = current;
}
return current;
}
// House Robber II: Houses in a circle (first and last are adjacent)
public static int robCircular(int[] nums) {
if (nums.length == 1) return nums[0];
// Case 1: Rob houses 0 to n-2 (exclude last)
int max1 = robRange(nums, 0, nums.length - 2);
// Case 2: Rob houses 1 to n-1 (exclude first)
int max2 = robRange(nums, 1, nums.length - 1);
return Math.max(max1, max2);
}
private static int robRange(int[] nums, int start, int end) {
int prev2 = 0;
int prev1 = 0;
int current = 0;
for (int i = start; i <= end; i++) {
current = Math.max(nums[i] + prev2, prev1);
prev2 = prev1;
prev1 = current;
}
return current;
}
public static void main(String[] args) {
int[] houses = {2, 7, 9, 3, 1};
System.out.println("Max money (linear): " + rob(houses));
// Output: 12 (rob houses 0, 2, 4: 2+9+1)
int[] circularHouses = {2, 3, 2};
System.out.println("Max money (circular): " + robCircular(circularHouses));
// Output: 3 (can't rob both 0 and 2 since they're adjacent in circle)
}
}

🔑 Key Points to Remember

  • 1️⃣Pattern: dp[i] = f(dp[i-1], dp[i-2], ...)
  • 2️⃣Always prefer space-optimized version (O(1) space)
  • 3️⃣Many problems are disguised Fibonacci!
  • 4️⃣Time: O(n), Space: O(1) with optimization

💪 Practice Problems

  • Min Cost Climbing Stairs
  • Delete and Earn
  • Decode Ways
  • Unique Paths
  • Jump Game