文章
问答
冒泡
爬楼梯问题

场景需求

假设你正在爬楼梯。需要 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阶楼梯的话 就仅仅只有1种方法,

2阶楼梯的话,我们可以第一步爬1阶,第二步爬1阶 + 第一步直接爬2阶 得到 2种解法。

3阶楼梯的话,那么方式就是 1+1+1 或者 1+2 或者 2+1 等于3种。

4阶楼梯的话 。。。。如果是这种思路的话 估计会累死


实际上只要有接触过动态规划概念的话,就会知道这种情况如何去处理了。下面聊下动态规划


动态规划

动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们得需要一些变量来保存,一般是用一维数组或者二维数组来保存。


对于动态规划可以总结为三部分步骤:

  1. 定义数组的含义。由于我们会通过数组来保存历史记录,简单举例通过一位数组来存放历史记录。dp[]【这个数组也是有考究的,因为这会牵扯到空间复杂度,针对不同的命题要求我们可以适当的应变】
  2. 找出数组之间的关系式。即利用历史数据我们可以推导出当前数组元素的依赖关系,结合上题,当有 n 阶台阶的时候,我们可以从 n-1 号台阶通过 1 阶爬上来,也可以通过 n-2 号台阶通过 2 阶台阶爬上来,那么实际上 dp[n] = dp[n-1] + dp[n-2]。 在这里需要注意的是,我们不要纠结于 n-1 号及 n-2 号台阶那么又有多少种,因为这是一个抽象迭代的过程。既然存在历史数据肯定是能够拿到的。通常来说 dp[n] 的关系式是解决问题的关键与难点。
  3. 结合步骤 2 我们知道,如果一直迭代下去,肯定是存在一个初始值,由此得出步骤3 找出初始值。


具体实现

有了以上对动态规划的初步了解以及要点步骤解析,结合题目再次梳理下:


定义数组 dp[n],来记录到 n 阶楼梯的方法数。


数组关联关系 dp[n] = dp[n-1]+dp[n-2],爬到 n 号阶梯我们结合题目可以得知 要么从 n-1 号楼梯上来,要么从 n-2 号楼梯上来,即为 爬到 n-1 号 楼梯的方法数 + 爬到 n-2 号楼梯的方法数。在针对处理当前问题的时候,我们的聚焦点是当前,不要关联其他状态,如果没有这一层抽象的理解,尤其在处理递归和迭代的算法问题中我们会把自己耗死,导致越想越深,一直死循环。


那么下面就是到了对初始状态的分析了:

  • dp[1] 是否满足 dp[0] + dp[-1],由于 n 是正整数,那么 不满足此关联表达式,那么应该手动初始化 dp[1] =1;
  • dp[2] 是否满足 dp[0] + dp [1], 由于 n 是正整数,也不满足此关联表达式,那么也应该手动初始化dp[2] = 2;
  • dp[3] 是否满足 dp [2] + dp[1],我们发现是满足的,那么由此往下 都满足了 表达式,那么由此可知,n >= 3 的时候就能过通过表达式来求值了。


代码实现


public static int climbStairs(int n) {
    // 为了与直接题解中表达的 dp[n] 表示为 n 阶台阶的爬法相对应,此时初始化数组的时候会浪费dp[0]
    int[] dp = new int[n+1];
    // 这里直接把初始化部分提取出来,作为边界直接输出
    if( n<3) {
        return n;
    }
    dp[1] = 1; // 如果想从下标为0 开始的话,这里可以表示为 dp[n-1] 为实际的 dp[n]。
    dp[2] = 2;
    for(int i=3;i<=n;i++) {
        dp[i] = dp[i-1] + dp[i-2] ;
    }
    return dp[n];
}


知识点: 动态规划

代码示例

数据结构与算法
动态规划

关于作者

Kirago
个人站点 https://kiragoo.github.io/
获得点赞
文章被阅读