题目

思路
第一秒想到是递归,且看到n最大是45觉得应该够,然后也这么写了。
javascript
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
if (n === 1) return 1
if (n === 2) return 2
return climbStairs(n-1) + climbStairs(n-2)
};
于是喜提超时。
老老实实写动态规划了。
代码
动态规划
代码随想录动规五部曲:
1.确定dp数组(dp table)以及下标的含义
dpi:爬到第i阶楼梯有dpi种方法
2.确定递推公式
可以倒着想,爬上第n阶台阶的话有两种方式,一种是从第n-1阶爬一阶上来,一种是从第n-2阶爬两阶上来。而第n-1阶和第n-2阶有多少种方式又分为两种往前推,所以显而易见的:dpn = dpn-1+dpn-2
3.dp数组如何初始化
题目说n是正整数,所以从1开始看就可以了。
n=1,dp1=1:一步
n=2,dp2=2:两步/两个一步
4.确定遍历顺序
从递推公式可知一定是从前往后遍历的。
5.举例推导dp数组
例如n=4时,dp数组应为:
n=1,dp1=1
n=2,dp2=2
n=3,dp3=dp1+dp2=3
n=4,dp4=dp2+dp3=5
javascript
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
if (n <= 2) return n
const dp=[]
dp[1] = 1
dp[2] = 2
for (let i = 3; i <= n; i++){
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
};
递归(记忆版)
但如果我硬要用递归呢()
其实我觉得本质上还是动态规划,只是写法比较"递归"。这种写法是考虑到递归要重复计算很多次,那我直接把算过的存起来不就好了。这还是跟动态规划一个思想,只不过动规是自底向上,而记忆版递归是自顶向下。
javascript
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
const memory = {}// 用来存算过的值
function dfs(n) {
if (n === 1) return 1
if (n === 2) return 2
// 算过的直接返回
if (memory[n] !== undefined) return memory[n]
// 没算过的加进去
memory[n] = dfs(n-1) + dfs(n-2)
return memory[n]
}
return dfs(n)
};