【每天学习一点算法 2025/12/25】爬楼梯

每天学习一点算法 2025/12/25

题目:爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

爬楼梯是一道十分经典的动态规划题目,我记得大学老师还讲过这道题,虽然已经忘的差不多了,我们首先来分析这题目,我们先从小数推导规律,设 fn(i) = 爬到第i阶的方法数

  • 爬 1 阶楼梯:只有一种方法,fn(1) = 1

  • 爬 2 阶楼梯:有 1+12两种方法, fn(2) = 2

  • 爬 3 阶楼梯:我们换一个思考方式,如果我们登上了第 3 阶楼梯的最后一步爬了 1 个台阶,那这种爬 3 阶楼梯的方法就和爬 2 阶楼梯的方法一样,如果最后一步爬了 2 个台阶,那这种爬 3 阶楼梯的方法就和爬 1 阶楼梯的方法一样。这两种爬最后一步的方法加在一起就是爬 3 阶的楼梯的方法数,

    fn(3) = f(2) + f(1) = 3

  • 爬 n 阶楼梯:从这个爬 3 阶楼梯的分析,我们很容易得出,爬 n 阶楼梯的方法就是,爬 n - 1 阶楼梯(最后一步是爬 1 个台阶)的方法加上爬 n - 2 阶楼梯(最后一步是爬 2 个台阶)的方法,fn(n) = fn(n - 1) + fn(n - 2)

分析得出了最终的公式: fn(n) = fn(n - 1) + fn(n - 2)

  1. 最容易想到的就是递归

    typescript 复制代码
    function climbStairs(n: number): number {
       if (n === 1) return 1
       if (n === 2) return 2
       return climbStairs(n - 1) + climbStairs(n - 2)
    };

    此法虽然很容易想到但是数字太大的时候会超时,而且存在大量的重复运算,比如我们在计算爬 5 阶楼梯和爬 4 阶的方法时,都会计算爬 3 阶楼梯的方法。

  2. 上面的递归存在重复计算,我们可以用记忆化递归的方法,用一个数组存储已经计算过的结果,这样就可以有效的减少时间复杂度了

    typescript 复制代码
    function climbStairs(n: number): number {
      const memoSet: number[] = [1, 1, 2] // 初始填入 0 1 2 阶的爬楼方法数
      function auxiliary (n: number, memo: number[]): number {
        // 如果已有爬楼的计算结果直接取出不再重复递归
        if (memo[n]) return memo[n]
        // 递归计算爬楼方法并存储到记忆数组中
        memo[n] = auxiliary(n - 1, memo) + auxiliary(n - 2, memo)
        return memo[n]
      }
      // 传入记忆数组
      return auxiliary(n, memoSet)
    };
  3. 接下来讲一下动态规划的解题方法,动态规划(Dynamic Programming,简称 DP)核心是用空间换时间,避免重复计算,本质是:把一个复杂的大问题,拆解成若干个可解决的、不重复的小问题,先解决小问题并记录答案(存起来),再用小问题的答案推导大问题的答案。

    什么意思呢?

    • 递归是从目标值递归拆解成小问题,从目标 n 递归拆解成 n-1n-2
    • 动态规划是从最小的初始条件开始,一步步算到目标值(比如从 i=3 算到 i=n
    typescript 复制代码
    function climbStairs(n: number): number {
      // 处理边界 1 2 阶楼梯爬法
      if (n <= 2) return n;
      const memoSet: number[] = [1, 1, 2] // 初始填入 0 1 2 阶的爬楼方法数
      // 循环计算n阶爬楼方法
      for (let i = 3; i <= n; i++) {
        memoSet[i] = memoSet[i - 1] + memoSet[i - 2]
      }
      // 返回计算结果
      return memoSet[n]
    };
  4. 上面的方法我们看出,前面已经用过的数据其实已经可以丢弃了,那我们还可以优化一下空间复杂度,用两个变量存储 n - 1n - 2 阶的方法数。

    typescript 复制代码
    function climbStairs(n: number): number {
    	// 处理边界 1 2 阶楼梯爬法
      if (n <= 2) return n;
      let i = 1 // n - 2 初始为1阶楼梯爬法
      let j = 2 // n - 3 初始为2阶楼梯爬法
      let current = 0 // 存储当前阶的方法数
      // 循环计算爬 n 阶方法数
      for (let k = 3; k <= n; k++) {
        current = i + j
        i = j
        j = current
      }
      return current
    };

题目来源:力扣(LeetCode)

相关推荐
草履虫建模13 分钟前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq2 小时前
分布式系统安全通信
开发语言·c++·算法
Jasmine_llq3 小时前
《P3157 [CQOI2011] 动态逆序对》
算法·cdq 分治·动态问题静态化+双向偏序统计·树状数组(高效统计元素大小关系·排序算法(预处理偏序和时间戳)·前缀和(合并单个贡献为总逆序对·动态问题静态化
ASKED_20193 小时前
Langchain学习笔记一 -基础模块以及架构概览
笔记·学习·langchain
爱吃rabbit的mq3 小时前
第09章:随机森林:集成学习的威力
算法·随机森林·集成学习
(❁´◡`❁)Jimmy(❁´◡`❁)4 小时前
Exgcd 学习笔记
笔记·学习·算法
YYuCChi4 小时前
代码随想录算法训练营第三十七天 | 52.携带研究材料(卡码网)、518.零钱兑换||、377.组合总和IV、57.爬楼梯(卡码网)
算法·动态规划
不能隔夜的咖喱5 小时前
牛客网刷题(2)
java·开发语言·算法
VT.馒头5 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
进击的小头5 小时前
实战案例:51单片机低功耗场景下的简易滤波实现
c语言·单片机·算法·51单片机