图解推导爬楼梯(跳台阶)问题详细过程

1,题目:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。,

2,递推公式(状态转移方程)推导

分析,需求比较简单。拿到这个题目的第一想法就是递归,但是这个递推公式是怎么得来的?我居然陷入了逻辑死胡同,花了很长时间才彻底绕出来了。

不多说,先上图:

图片链接:上楼梯(1次上1台或者2台)问题 | ProcessOn免费在线作图,在线流程图,在线思维导图 |

认真的看上图,可以看出:

上到第1台台阶:
一共有1种方法:即: f(1) = 1

上到第2台台阶:
一共有f(2)种方法:即: f(2) = 2。其实这里也是 f(2) = f(1)的方法上网上走一台,在加另外一种方法,直接从地面迈2台台阶,有就是 f(2) = f(1) +1

上到第3台台阶:
1,先上到第1台,用了f(1)种方法,然后直接跨2步到第3台,用了的方法数还是f(1)
2,先上到第2台,用了f(2)种方法,然后直接跨1步到第3台,用了的方法数还是f(2)
因此方法数为: f(3) = f(1) + f(2)

上到第4台台阶:
1,先上到第2台,用了f(2)种方法,然后直接跨2步到第4台,用了的方法数还是f(2)
2,先上到第3台,用了f(3)种方法,然后直接跨1步到第4台,用了的方法数还是f(3)
因此方法数为: f(4) = f(4-2) + f(4-1)

上到第5台台阶:
1,先上到第3台,用了f(3)种方法,然后直接跨2步到第5台,用了的方法数还是f(3)
2,先上到第4台,用了f(4)种方法,然后直接跨1步到第5台,用了的方法数还是f(4)
因此方法数为: f(5) = f(5-2) + f(5-1)

上到第n台台阶:
1,先上到第n-2台,用了f(n-2)种方法,然后直接跨2步到第n台,用了的方法数还是f(n-2)
2,先上到第n-1台,用了f(n-1)种方法,然后直接跨1步到第n台,用了的方法数还是f(n-1)
因此方法数为: f(n) = f(n-2) + f(n-1)

因此,递推公式是: f(n) = f(n-2) + f(n-1)

我一开始的时候理解成了:

从第 n - 2台台阶上去的时候,还要迈一次(2台),从n-1上到n的时候,还要迈一次(1台),因此错误的认为,总方法数是 f(n) = [f(n-2) +2 ]+  [f(n-1) + 1],将方法和迈的步数搞混了。

从f(n-2)台阶迈到n台台阶(这里只能一次迈2台,否则会重复为f(n-1) ),走到f(n-2)的每一种方法再都迈一次2台台阶到达n台台阶,此时用的方法数还是f(n-2)种。同理,走到f(n-1)的每一种方法再都迈一次1台台阶到达n台台阶,此时用的方法数还是f(n-1)种。走到第n台台阶,只可能从n-2台上来,或者从n-1上来。所以,正确的递推公式是:  f(n) = f(n-2) + f(n-1)

有了递推公式代码就好写了:

3,递归+备忘录求解

/**
 * 递归方式求解求爬n台台阶的总方法数
 * 递推公式,方法总数: f(n) = f(n-2) + f(n-1)
 * @param {*} n 
 * @returns 爬n台台阶的总方法数
 */
function climbStairs(n) {
    const cache = new Map()
    /**
     * 内部递归方法,递归方式求解爬n台台阶的所有方法数
     * @param {*} n 
     * @returns 
     */
    function climb(n) {
        if(n === 0) return 0
        if(n === 1) return 1
        if(n === 2) return 2
        // 使用备忘录缓存
        const cachedN = cache.get(n)
        if(cachedN !== undefined) {
            return cachedN
        }
        const result = climb(n-2) + climb(n-1)
        cache.set(n, result) // 存入缓存
        return result        
    }
    return climb(n)
}

4,动态规划求解

有了递推公式,状态转移方程也就很容易写了:

dp[n] =  dp[n-2] + dp[n-1]; dp[0] = 0|1; dp[1] = 1; dp[2] = 2;

/**
 * 动态规划方式求解,从底(小)向上解
 * 时间复杂度: O(n)
 * 空间复杂度: O(n)
 * 最核心的步骤也是状态转移方程,跟递归的递推公式差不多
 * dp[n] =  dp[n-2] + dp[n-1]; dp[0] = 0|1; dp[1] = 1; dp[2] = 2;
 * @param {*} n 要上的总台阶数
 * @returns 爬n台台阶的总方法数
 */
function climbStairsDP(n) {
    const dp = []
    dp[0] = 0
    dp[1] = 1
    dp[2] = 2
    for(let i=3; i<=n; i++) {
        dp[i] = dp[i-1] + dp[i-2]
    }
    return dp[n]
}

5,优化的动态规划版本求解

/**
 * 动态规划方式求解爬楼梯问题,从底(小)向上解。基于上面的方法优化状态记录,从数组状态记录优化为三个变量,优化存储空间
 * 时间复杂度: O(n)
 * 空间复杂度: O(1)
 * 原状态: dp[n] =  dp[n-2] + dp[n-1]; dp[0] = 0|1; dp[1] = 1; dp[2] = 2;
 * 压缩为: next = pre + current; pre = 1, current = 2, next; pre、current、next滚动更新
 * @param {*} n 要上的总台阶数
 * @returns 爬n台台阶的总方法数
 */
function climbStairsDP2(n) {
    let pre = 1, current = 2, next; //将数组状态滑动为三个变量状态
    for(let i=3; i<=n; i++) {
        next = pre + current
        // 滑动更新变量状态
        pre = current
        current = next
    }
    return next
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值