目录
经过上一篇文章的学习,相信大家其实已经比较理解动态规划的整个流程了,这道题也不算是很那的题目,但是如果是使用递归或者其他方法的话,我个人认为是有点麻烦而且效率并不高,动态规划的代码一般都是比较简短的,只要写出了递推公式,那其实这道题也已经解决了一半了,等你再定义好dp数组,再将数组初始化好那其实基本上已经拿下了,让我们来看看这道题吧。
力扣题号:70. 爬楼梯 - 力扣(LeetCode)
注:下述题目描述和示例均来自力扣
题目描述
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例
示例 1:
输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶
示例 2:
输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶
提示
1 <= n <= 45
思路
解法一:动态规划
为什么使用动态规划
其实写一道编程题其中的一个很重要的步骤就是你能正确的找到要使用的方法对吧。那这里为什么使用动态规划呢?
我先举个例子,如果你要寻找到达第三阶楼梯的方法,那么你是需要根据到第二阶楼梯和第一阶楼梯有多少中方法推导出来,从第二阶你可以走一阶到达第三节,也可以从第一阶走两阶到第三阶,这就是为什么我们要使用动态规划,因为你可以通过前两阶来判断到达这一阶的方法数。
五步走
(1)确定递归数组以及下标的含义
我们这里很明显了,递归数组的下标的含义明显是到达当前 i 阶楼梯的方法数。
(2)确定递归公式
根据前面的解释,这里的递推公式也是显而易见的,如下:
(3)初始化递归数组
这里其实递归数组的初始化还是要思考思考的,我们可以先观察到,n是至少有一阶的,那么到一阶只有 1 种方法, 到达第二阶有 1 + 1阶和 2阶 这两种方法,所以dp[1] = 1,
dp[2] = 2;就确定下来了。
(4)确定遍历的顺序
我们需要前面的数据,所以遍历的顺序显然是从前往后了。
(5)举例推导dp数组
那自己稍微根据题意推导一下就可以得到如下的dp数组了(n = 6):
那把上面的思路代码化就解决了。
标准代码实现
class Solution {
public int climbStairs(int n) {
// 动态规划
if(n < 3){
return n;
}
// dp[i]:表示到当前阶梯数的方法数量
// 递推公式 dp[i] = dp[i - 1] + dp[i - 2]
int[] dp = new int[n + 1];
// 初始化
dp[1] = 1;
dp[2] = 2;
for(int i = 3; i <= n; i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
}
省内存修改
当然了,这里其实和上一篇文章斐波那契数一样,都只需要前两个来操作,所以我们可以只记录 i 和 i - 1以及 i - 2 的值。
代码如下:
class Solution {
public int climbStairs(int n) {
// 动态规划
if(n < 3){
return n;
}
// dp[i]:表示到当前阶梯数的方法数量
// 递推公式 dp[i] = dp[i - 1] + dp[i - 2]
// 初始化
int dp1 = 1;
int dp2 = 2;
int res = 0;
for(int i = 3; i <= n; i++){
res = dp1 + dp2;
dp1 = dp2;
dp2 = res;
}
return res;
}
}
并不省内存是吧,这应该是力扣的测试用例的值太小了。如果n能到这种级别(只是夸张处理便于理解),这种肯定更省内存,毕竟只需要存储三个数,而dp数组要全部存储。
总结
越来越熟练喽老铁,啦啦啦啦啦啦,开心^_^。