【每天学习一点算法 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)

相关推荐
m0_736919106 分钟前
C++中的委托构造函数
开发语言·c++·算法
小小小小王王王12 分钟前
洛谷-P1886 【模板】单调队列 / 滑动窗口
c++·算法
PPPPPaPeR.38 分钟前
光学算法实战:深度解析镜片厚度对前后表面折射/反射的影响(纯Python实现)
开发语言·python·数码相机·算法
看我干嘛!42 分钟前
python第五次作业
算法
历程里程碑1 小时前
Linux 库
java·linux·运维·服务器·数据结构·c++·算法
Sheep Shaun1 小时前
如何让一个进程诞生、工作、终止并等待回收?——探索Linux进程控制与Shell的诞生
linux·服务器·数据结构·c++·算法·shell·进程控制
Pluchon1 小时前
硅基计划4.0 简单模拟实现AVL树&红黑树
java·数据结构·算法
生锈的键盘1 小时前
推荐算法实践:交叉特征的理解
算法
试着1 小时前
【huawei】机考整理
学习·华为·面试·机试
風清掦1 小时前
【江科大STM32学习笔记-05】EXTI外部中断11
笔记·stm32·学习