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

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

题目:爬楼梯

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

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

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

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

  • 爬 2 阶楼梯:有 1+1 、2两种方法, 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-1 和 n-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 - 1 和 n - 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)

相关推荐
一隅论数智3 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
XiHongShi20163 天前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
爱吃苹果的日记本3 天前
离散数学第六课
学习·离散数学
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
AI职业加油站3 天前
AI智能体应用工程师证书:政策红利下的职业新风口
大数据·运维·人工智能·学习·职场发展
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法