Fibonacci & Climbing Stairs
Fibonacci and Climbing Stairs are the gateway to 1D dynamic programming. Both use the same recurrence—each step depends on the previous one or two results. This lesson covers the theory, tabulation traces, and full implementations in C, Java, and Python with output explanation tables.
Overview
These problems teach the same DP pattern:
State: dp[i] = answer for subproblem of size i Recurrence: dp[i] = dp[i-1] + dp[i-2] (or min/max variant) Base: dp[0], dp[1] known Goal: dp[n]
| Aspect | Fibonacci | Climbing Stairs |
|---|---|---|
| Question | What is fib(n)? | How many ways to climb n steps (1 or 2 at a time)? |
| Recurrence | fib(n) = fib(n−1) + fib(n−2) | ways(n) = ways(n−1) + ways(n−2) |
| Base cases | fib(0)=0, fib(1)=1 | ways(1)=1, ways(2)=2 |
| Relation | — | ways(n) = fib(n+1) with fib(1)=1, fib(2)=2 |
| LeetCode | 509 | 70 |
| Time / Space | O(n) / O(1) optimized | O(n) / O(1) optimized |
Fibonacci Problem
Compute the n-th Fibonacci number where F(0)=0, F(1)=1, and F(n)=F(n−1)+F(n−2).
DP state: dp[i] = Fibonacci number at index i.
Why DP? Naive recursion recomputes F(2), F(3), … many times. Storing each value once reduces time from O(2ⁿ) to O(n).
Fibonacci Code (C, Java, Python)
Bottom-up tabulation with O(1) space—preferred for interviews and production.
Python
def fibonacci(n: int) -> int:
"""Return n-th Fibonacci number — bottom-up DP, O(n) time, O(1) space."""
# Base cases: F(0) = 0, F(1) = 1
if n <= 1:
return n
# Rolling window: prev2 = F(i-2), prev1 = F(i-1)
prev2, prev1 = 0, 1
for _ in range(2, n + 1):
curr = prev1 + prev2 # F(i) = F(i-1) + F(i-2)
prev2, prev1 = prev1, curr # slide window forward
return prev1 # F(n)
if __name__ == "__main__":
# Demo output for sample inputs
for n in [0, 1, 5, 8, 10]:
print(f"fib({n}) = {fibonacci(n)}")
Java
public class Fibonacci {
/**
* Bottom-up Fibonacci with O(1) extra space.
* prev2 = F(i-2), prev1 = F(i-1)
*/
public static int fibonacci(int n) {
// Base cases
if (n <= 1) return n;
int prev2 = 0; // F(0)
int prev1 = 1; // F(1)
for (int i = 2; i <= n; i++) {
int curr = prev1 + prev2; // F(i) = F(i-1) + F(i-2)
prev2 = prev1;
prev1 = curr;
}
return prev1; // F(n)
}
public static void main(String[] args) {
int[] tests = {0, 1, 5, 8, 10};
for (int n : tests) {
System.out.println("fib(" + n + ") = " + fibonacci(n));
}
}
}
C
#include <stdio.h>
/* Bottom-up Fibonacci: O(n) time, O(1) space */
int fibonacci(int n) {
if (n <= 1) return n; /* base cases F(0), F(1) */
int prev2 = 0; /* F(i-2) */
int prev1 = 1; /* F(i-1) */
int curr, i;
for (i = 2; i <= n; i++) {
curr = prev1 + prev2; /* F(i) = F(i-1) + F(i-2) */
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
int main(void) {
int tests[] = {0, 1, 5, 8, 10};
int i, n, len = sizeof(tests) / sizeof(tests[0]);
for (i = 0; i < len; i++) {
n = tests[i];
printf("fib(%d) = %d\n", n, fibonacci(n));
}
return 0;
}
Fibonacci Output & Trace Tables
Program output (all three languages)
| Language | Input (n values) | Console Output | Explanation |
|---|---|---|---|
| Python | 0, 1, 5, 8, 10 | fib(0)=0 fib(1)=1 fib(5)=5 fib(8)=21 fib(10)=55 | Loop runs n−1 times for n≥2; base cases return immediately |
| Java | 0, 1, 5, 8, 10 | fib(0) = 0 fib(1) = 1 fib(5) = 5 fib(8) = 21 fib(10) = 55 | Same logic; println adds spaces around = |
| C | 0, 1, 5, 8, 10 | fib(0) = 0 fib(1) = 1 fib(5) = 5 fib(8) = 21 fib(10) = 55 | printf format; integer return type |
Input → output reference
| Input n | Output fib(n) | How it is computed | Loop iterations (n ≥ 2) |
|---|---|---|---|
| 0 | 0 | Base case: return n directly | 0 |
| 1 | 1 | Base case: return n directly | 0 |
| 2 | 1 | 0 + 1 = 1 | 1 |
| 3 | 2 | 1 + 1 = 2 | 2 |
| 4 | 3 | 2 + 1 = 3 | 3 |
| 5 | 5 | 3 + 2 = 5 | 4 |
| 8 | 21 | 13 + 8 = 21 | 7 |
| 10 | 55 | 34 + 21 = 55 | 9 |
DP table trace for n = 6 (full tabulation view)
| Index i | dp[i] | Formula | Variables after step (prev2, prev1) |
|---|---|---|---|
| 0 | 0 | Base | (0, 1) initial |
| 1 | 1 | Base | (0, 1) initial |
| 2 | 1 | dp[1]+dp[0] = 1+0 | (1, 1) |
| 3 | 2 | dp[2]+dp[1] = 1+1 | (1, 2) |
| 4 | 3 | dp[3]+dp[2] = 2+1 | (2, 3) |
| 5 | 5 | dp[4]+dp[3] = 3+2 | (3, 5) |
| 6 | 8 | dp[5]+dp[4] = 5+3 | (5, 8) → return 8 |
Climbing Stairs Problem
You can climb 1 or 2 steps at a time. How many distinct ways to reach the top of n steps?
DP state: dp[i] = number of ways to reach step i.
Recurrence: To reach step i, you came from step i−1 (1-step) or i−2 (2-step):
dp[i] = dp[i-1] + dp[i-2] dp[1] = 1, dp[2] = 2
Climbing Stairs Code (C, Java, Python)
Python
def climb_stairs(n: int) -> int:
"""Count ways to climb n steps (1 or 2 at a time). Same pattern as Fibonacci."""
# Base: 1 step → 1 way, 2 steps → 2 ways
if n <= 2:
return n
prev2, prev1 = 1, 2 # ways(1), ways(2)
for _ in range(3, n + 1):
curr = prev1 + prev2 # ways(i) = ways(i-1) + ways(i-2)
prev2, prev1 = prev1, curr
return prev1
if __name__ == "__main__":
for n in [1, 2, 3, 4, 5, 6]:
print(f"ways({n}) = {climb_stairs(n)}")
Java
public class ClimbingStairs {
/** LeetCode 70 — distinct ways to climb n steps (1 or 2 each move). */
public static int climbStairs(int n) {
if (n <= 2) return n; // base cases
int prev2 = 1; // ways to reach step 1
int prev1 = 2; // ways to reach step 2
for (int i = 3; i <= n; i++) {
int curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
public static void main(String[] args) {
for (int n = 1; n <= 6; n++) {
System.out.println("ways(" + n + ") = " + climbStairs(n));
}
}
}
C
#include <stdio.h>
/* Climbing stairs — ways(n) = ways(n-1) + ways(n-2) */
int climb_stairs(int n) {
if (n <= 2) return n;
int prev2 = 1; /* ways(1) */
int prev1 = 2; /* ways(2) */
int curr, i;
for (i = 3; i <= n; i++) {
curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
int main(void) {
int n;
for (n = 1; n <= 6; n++) {
printf("ways(%d) = %d\n", n, climb_stairs(n));
}
return 0;
}
Climbing Stairs Output & Trace Tables
Program output (all three languages)
| Language | Input range | Console Output | Explanation |
|---|---|---|---|
| Python | n = 1 … 6 | ways(1)=1 ways(2)=2 ways(3)=3 ways(4)=5 ways(5)=8 ways(6)=13 | Matches Fibonacci shifted: ways(n)=fib(n+1) |
| Java | n = 1 … 6 | ways(1) = 1 … ways(6) = 13 | Same sequence; loop from 3 to n for n > 2 |
| C | n = 1 … 6 | ways(1) = 1 … ways(6) = 13 | Identical logic to Java/Python |
Input → output with valid paths
| Input n (steps) | Output (ways) | All distinct paths | Explanation |
|---|---|---|---|
| 1 | 1 | [1] | Only one 1-step move |
| 2 | 2 | [1+1], [2] | Two choices: two 1-steps or one 2-step |
| 3 | 3 | [1+1+1], [1+2], [2+1] | ways(2)+ways(1) = 2+1 = 3 |
| 4 | 5 | 5 combinations | ways(3)+ways(2) = 3+2 = 5 |
| 5 | 8 | 8 combinations | ways(4)+ways(3) = 5+3 = 8 |
| 6 | 13 | 13 combinations | ways(5)+ways(4) = 8+5 = 13 |
DP table trace for n = 5
| Step i | dp[i] (ways) | Recurrence used | Meaning |
|---|---|---|---|
| 1 | 1 | Base | One way to reach first step |
| 2 | 2 | Base | 1+1 or 2 |
| 3 | 3 | dp[2]+dp[1] | Arrive from step 2 or step 1 |
| 4 | 5 | dp[3]+dp[2] | 3 + 2 = 5 total paths |
| 5 | 8 | dp[4]+dp[3] | 5 + 3 = 8 → final answer |
Min Cost Climbing Stairs
LeetCode 746: Each step has a cost; you may start at index 0 or 1 and pay to leave a step. Find minimum cost to reach the top (beyond last index).
Recurrence: dp[i] = cost[i] + min(dp[i−1], dp[i−2])
Python
def min_cost_climbing_stairs(cost):
"""LeetCode 746 — min cost to reach top; can start at step 0 or 1."""
n = len(cost)
prev2, prev1 = cost[0], cost[1] # min cost to leave step 0 and 1
for i in range(2, n):
# Pay cost[i] plus cheaper path from previous two steps
curr = cost[i] + min(prev1, prev2)
prev2, prev1 = prev1, curr
# Jump to top from either last step (no extra cost)
return min(prev1, prev2)
# Demo: cost = [10, 15, 20] → output 15
print(min_cost_climbing_stairs([10, 15, 20]))
Java
public class MinCostStairs {
/** Min cost to reach top — start at index 0 or 1, pay to leave each step. */
public static int minCostClimbingStairs(int[] cost) {
int prev2 = cost[0];
int prev1 = cost[1];
for (int i = 2; i < cost.length; i++) {
int curr = cost[i] + Math.min(prev1, prev2);
prev2 = prev1;
prev1 = curr;
}
// Final hop to top costs nothing extra
return Math.min(prev1, prev2);
}
public static void main(String[] args) {
int[] cost = {10, 15, 20};
System.out.println(minCostClimbingStairs(cost)); // 15
}
}
C
#include <stdio.h>
/* Min cost climbing stairs — O(n) time, O(1) space */
int min_cost(int cost[], int n) {
int prev2 = cost[0];
int prev1 = cost[1];
int curr, i;
for (i = 2; i < n; i++) {
curr = cost[i] + (prev1 < prev2 ? prev1 : prev2);
prev2 = prev1;
prev1 = curr;
}
return prev1 < prev2 ? prev1 : prev2; /* cheapest finish */
}
int main(void) {
int cost[] = {10, 15, 20};
printf("%d\n", min_cost(cost, 3)); /* output: 15 */
return 0;
}
Min cost output & trace — cost = [10, 15, 20]
| Language | Input | Output | Explanation |
|---|---|---|---|
| Python | [10, 15, 20] | 15 | Start at index 1 (cost 15), jump to top — cheaper than 10+20 |
| Java | {10, 15, 20} | 15 | Math.min(15, 30) at end |
| C | {10, 15, 20} | 15 | Same min path; printed with printf |
| Index i | cost[i] | dp[i] (min cost to leave i) | Best path so far |
|---|---|---|---|
| 0 | 10 | 10 | Start here: pay 10 |
| 1 | 15 | 15 | Start here: pay 15 (cheaper start) |
| 2 | 20 | 20+min(10,15)=35 | 0→2 costs 30; 1→2 costs 35 |
| Top | — | min(15,35)=15 | Jump from step 1 to top → answer 15 |
More Examples
Example 1: Tribonacci (3-term recurrence)
T(n) = T(n−1) + T(n−2) + T(n−3). Same 1D DP pattern with three rolling variables.
| Input n | Output T(n) | Recurrence | Notes |
|---|---|---|---|
| 0 | 0 | Base | T(0)=0 |
| 1 | 1 | Base | T(1)=1 |
| 2 | 1 | Base | T(2)=1 |
| 3 | 2 | 1+1+0 | First computed value |
| 4 | 4 | 2+1+1 | — |
| 5 | 7 | 4+2+1 | — |
Example 2: Climbing stairs with step sizes {1, 2, 3}
Recurrence becomes dp[i] = dp[i−1] + dp[i−2] + dp[i−3].
| Input n | Output (ways) | Formula | Explanation |
|---|---|---|---|
| 1 | 1 | Base | [1] |
| 2 | 2 | Base | [1+1], [2] |
| 3 | 4 | 2+1+1 | Add 3-step option: [3], [1+2], [2+1], [1+1+1] |
| 4 | 7 | 4+2+1 | Generalized Fibonacci |
| 5 | 13 | 7+4+2 | O(n) time, O(1) with 3 variables |
Example 3: Decode Ways (same 1D structure)
String decoding counts valid splits—still linear DP: dp[i] depends on previous one or two positions. See coin change and string DP topics next in the series.
Example 4: Naive vs DP runtime (Fibonacci n = 30)
| Approach | Input n | Approx. recursive calls / ops | Output | Explanation |
|---|---|---|---|---|
| Naive recursion | 30 | ~2ⁿ ≈ 1 billion | 832040 | Hang or timeout on large n |
| Memoization | 30 | ~60 calls | 832040 | Each index computed once |
| Tabulation O(1) space | 30 | 29 iterations | 832040 | Fastest in practice |
Approach Comparison
| Approach | Time | Space | When to use |
|---|---|---|---|
| Naive recursion | O(2ⁿ) | O(n) stack | Never in interviews (teaching only) |
| Memoization | O(n) | O(n) cache + stack | Quick draft from recurrence |
| Tabulation (array) | O(n) | O(n) | Easy to trace on whiteboard |
| Tabulation (2 vars) | O(n) | O(1) | Best final answer for Fibonacci / stairs |
| Matrix exponentiation | O(log n) | O(1) | Advanced; huge n only |
Real-Life Applications
Fibonacci and climbing-stairs DP count linear sequences of choices where each step depends on the previous one or two outcomes—growth, paths, and costs in the real world follow the same recurrence.
| Domain | Real scenario | DP mapping | Pattern |
|---|---|---|---|
| Biology (historical) | Rabbit population growth — Fibonacci’s original 1202 puzzle | Each pair breeds after one month; pairs at month n = F(n) | Fibonacci |
| Architecture | Counting ways to climb a staircase with 1- or 2-step treads | Steps = n; moves = {1, 2}; ways = dp[n] | Climbing stairs |
| Urban planning | Walkways with single or double-width paving blocks | Block sizes = step sizes; path length = n | Climbing stairs |
| Telecom / encoding | Counting valid bit strings without consecutive 1s (LeetCode 600) | dp[i] = ways to fill length i; add 0 always, add 1 if previous was 0 | Fibonacci variant |
| Computer science | Naive Fibonacci tree height in divide-and-conquer analysis | Explains why memoization cuts calls from O(2ⁿ) to O(n) | Overlapping subproblems |
| Finance | Compound growth with two contribution options per period | State combines two prior period values | Fibonacci-like |
| Energy / facilities | Escalator or elevator stops with per-step operating cost | cost[i] on step i; min cost to top = min(dp[i-1], dp[i-2]) + cost[i] | Min cost climbing |
| Sports / fitness | Training plans: rest day or active day sequences over n weeks | Two choices per week → ways to schedule = dp[n] | Climbing stairs |
Interview Tips
- State the recurrence and base cases before coding.
- Mention naive O(2ⁿ) first, then fix with DP.
- For climbing stairs, note it equals shifted Fibonacci.
- Offer O(1) space optimization with two (or three) variables.
- Trace n=4 or n=5 on the whiteboard using a small table.
- Know min-cost variant — same pattern, min instead of sum.
Key Takeaway
Fibonacci and climbing stairs share the same 1D linear DP skeleton: define dp[i], combine two previous states, optimize to O(1) space. Master the trace tables—interviewers often ask you to walk through n=5 step by step.
Next up: Coin change problems · DP properties & complexity · Memoization vs tabulation