假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
方法一 —— 记忆化递归
由于递归太耗时,可以用记忆化递归避免重复的计算。
解题过程:
1.先对n为0这种特殊情况进行处理,然后n为1和2时直接return即可
2.memo数组:存储中间结果,避免重复计算
3.接下来就是判断memo[n]是否存在,如果计算过即存在,直接返回,无需重复计算;若不存在,则进行递归计算,为前两个之和。
代码
/**
* @param {number} n
* @return {number}
*/
const memo = []
var climbStairs = function(n) {
if( n === 0) return 1
if(n <= 3) return n
if(memo[n]) {
return memo[n]
}else {
memo[n] = climbStairs(n-1) + climbStairs(n-2)
}
return memo[n]
}
方法二 —— 动态规划(常规)
解题过程:
1.如果只有0阶或者1阶,则直接返回n。(后面直接从2阶及以上开始,防止0/1阶导致数组出现空指针)
2.dp[i]:爬到i层楼梯,有dp[i]种方法
3.从i=3开始遍历,因为必须要有前两个存在,且存在dp[n] = dp[n-1] + dp[n-2]的关系。
代码
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
if (n <= 1) return n
const dp = new Array(n)
dp[1] = 1
dp[2] = 2
for (let i = 3; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
}
方法三 —— 动态规划(滚动数组思想 ==> 优化空间为O(1))
解题过程:
1.先定义好三个常量,利用向左移动的思想,使得计算过程中只使用了常数个变量,且存在r = p + q的关系
2.只用常数个变量作为辅助空间,故渐进空间复杂度为 O(1)
代码
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
let p = 0, q = 0, r = 1
for (let i = 1; i<= n; i++) {
p = q
q = r
r = p + q
}
return r
};
方法四 —— 动态规划(非数组法)
目前所有的动态规划都有这两种写法:用数组或非数组。不用数组的也可以理解成滑动窗口、双指针。
解题过程:
1.当有0、1、2个台阶时,分别有0、1、2种方法,用return直接返回
2.prev用于记录前一个台阶,初始值为第一个台阶,curr用于记录当前台阶,初始值为第二个台阶
3.然后从第三个台阶开始循环,执行到第n个台阶。
代码
var climbStairs = function(n) {
if(n <= 2) {
return n
}
let prev = 1
let curr = 2
for(let i = 3;i <= n; i++) {
let temp = prev + curr
prev = curr
curr = temp
}
return curr
}