The brute-force solution uses naive recursion, leading to:
memo
to store the number of ways to climb i
steps for specific values of i
. It is initialized with base cases: memo[1] = 1
(there is 1 way to climb 1 step: take one 1-step) and memo[2] = 2
(there are 2 ways to climb 2 steps: two 1-steps or one 2-step).f(n)
that computes the number of ways to climb n
steps:
n
is in memo
, return the stored value memo[n]
. This avoids recomputing results for previously solved subproblems.n
is not in memo
, compute the number of ways to climb n
steps by summing the ways to climb n-2
steps (followed by a 2-step) and n-1
steps (followed by a 1-step). Store the result in memo[n]
as f(n-2) + f(n-1)
and return it.f(n)
with the input n
and return the result, which represents the total number of distinct ways to climb n
steps.
n == 1
, there is only 1 way to climb 1 step (take one 1-step), so return 1. If n == 2
, there are 2 ways to climb 2 steps (two 1-steps or one 2-step), so return 2. These checks handle the smallest inputs directly.dp
of size n
to store the number of ways to climb i+1
steps at index i
. Initialize dp[0] = 1
(ways to climb 1 step) and dp[1] = 2
(ways to climb 2 steps) as the base cases for the dynamic programming approach.i
from 2 to n-1
(corresponding to 3 to n
steps):
dp[i]
as the sum of dp[i-2]
(ways to climb i-1
steps, followed by a 2-step) and dp[i-1]
(ways to climb i
steps, followed by a 1-step).i+1
, you can come from step i-1
(via a 2-step) or step i
(via a 1-step).dp[n-1]
, which represents the number of ways to climb n
steps.
dp
array of size n
, the solution introduces two variables:
two_back
: Initialized to 1, representing the number of ways to climb 1 step (equivalent to dp[0]
).one_back
: Initialized to 2, representing the number of ways to climb 2 steps (equivalent to dp[1]
).i = 2
to n-1
is retained, but instead of filling a dp
array, it computes the number of ways for the current step using the variables:
next_num = two_back + one_back
, which represents the number of ways to climb i+1
steps (equivalent to dp[i]
in the first solution).two_back = one_back
(shift the previous step’s value) and one_back = next_num
(store the current step’s value).dp[i] = dp[i-2] + dp[i-1]
but only keeps the two values needed at each step.one_back
holds the number of ways to climb n
steps (equivalent to dp[n-1]
), so we return one_back
.
The Fibonacci Number problem asks you to compute the nth
number in the famous Fibonacci sequence, where each number is the sum of the two preceding ones. The sequence starts with 0 and 1, and is defined as:
F(0) = 0, F(1) = 1, F(n) = F(n - 1) + F(n - 2)
for n ≥ 2
.
This is a fundamental dynamic programming problem and appears frequently in interviews and algorithm practice.
A naive solution uses direct recursion: for each call to fib(n)
, you recursively calculate fib(n - 1)
and fib(n - 2)
. While conceptually simple, this approach is highly inefficient.
The recursive tree grows exponentially in size, and the same values are recalculated many times. For example, to compute fib(5)
, fib(3)
and fib(2)
are each called multiple times.
This leads to a time complexity of O(2n), making it infeasible for larger input values like n = 40
or higher.
To avoid recomputation, we can use a bottom-up iterative approach that builds the solution from the ground up. This technique eliminates recursion and uses just two variables to hold the previous Fibonacci values.
We initialize prev = 0
and cur = 1
. Then for each step from 2 to n
, we update cur
to be prev + cur
and shift prev
to the old value of cur
.
This results in a time complexity of O(n) and a space complexity of O(1), making it extremely efficient and scalable.