16
loading...
This website collects cookies to deliver better user experience
You are climbing a staircase. It takes n
steps to reach the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
n
steps, can be expressed as countDistinctPaths(n)
. n-1
steps. Similarly, if we climb two steps, we again need to follow the same rules to climb the remaining n-2
steps. countDistinctPaths(n-1)
paths for the remaining n-1
steps, or by taking two steps and then taking one of the countDistinctPaths(n-2)
paths for the remaining n-2
steps. countDistinctPaths(n) = countDistinctPaths(n-1) + countDistinctPaths(n-2)
n-1
and n-2
, then we can combine those answers to calculate the answer for n
. countDistinctPaths(0) = 0
(no staircase, so no way to climb it!) countDistinctPaths(1) = 1
(only one step, only one way to climb it) countDistinctPaths(n) = countDistinctPaths(n-1) + countDistinctPaths(n-2)
countDistinctPaths
if we want the answer of say a staircase with 5 steps.countDistinctPaths(5)
. countDistinctPaths(5) = countDistinctPaths(4) + countDistinctPaths(3)
. countDistinctPaths(4)
into countDistinctPaths(4) = countDistinctPaths(3) + countDistinctPaths(2)
. countDistinctPaths(5) = (countDistinctPaths(3) + countDistinctPaths(2)) + countDistinctPaths(3)
.countDistinctPaths(3)
with countDistinctPaths(2) + countDistinctPaths(1)
.countDistinctPaths(5) = (countDistinctPaths(3) + countDistinctPaths(2)) + (countDistinctPaths(2) + countDistinctPaths(1))
.countDistinctPaths(3)
and countDistinctPaths(2)
multiple times. countDistinctPaths
and the value in the node is the parameter we are passing to the function. As you can see, we're repeating the calls for 2 and 3. This tells us that the problem we are trying to solve has overlapping subproblems. countDistinctPaths(n)
, we need the optimal solution for countDistinctPaths(n-1)
and countDistinctPaths(n-2)
. In this particular context, "optimal" for us means the maximum number of paths. Therefore, this problem has an optimal substructure. function countDistinctPaths(n) {
// If there are no stairs
if (n === 0) {
return 0;
}
// If there is only one stair
if (n === 1) {
return 1;
}
// The recurrence relation
return countDistinctPaths(n-1) + countDistinctPaths(n-2);
}
n
so we're wasting a lot of time!// We've added "memo"ry to store values we compute. It is empty in the beginning.
function countDistinctPaths(n, memo={}) {
// If there are no stairs
if (n === 0) {
return 0;
}
// If there is only one stair
if (n === 1) {
return 1;
}
// Check if we have already computed this before
if (n in memo) {
return memo[n];
}
// The recurrence relation with two minor tweaks:
// 1. We store the computed value in memo[n] for future use
// 2. We pass the memory object to the recursive calls
return memo[n] = countDistinctPaths(n-1, memo) + countDistinctPaths(n-2, memo);
}
countDistinctPaths(n)
and keep recursively breaking it down into smaller problems until we reach the smallest possible subproblems - countDistinctPaths(0)
and countDistinctPaths(1)
. countDistinctPaths(0)
and countDistinctPaths(1)
to which we know the answer, and then combine them to get the answer to countDistinctPaths(2)
and then countDistinctPaths(3)
and so on until we find the answer to countDistinctPaths(n)
. countDistinctPaths(n)
for each value of n
. This storage is commonly known as a "table" and this bottom-up approach is also commonly called "tabulation". function countDistinctPaths(n) {
// Our "table" to store the result for each value of n
const table = new Array(n + 1);
// The case with no stairs
table[0] = 0;
// The case with one stair
table[1] = 1;
// We keep combining subproblems until we find a solution to the original problem
for (let i = 2; i <= n; i++) {
table[i] = table[i-1] + table[i-2];
}
return table[n];
}
n
of the table.