爬楼梯

爬楼梯问题是一个经典的动态规划问题,通常表述为:一个人站在楼梯的底部,每次可以选择爬 1 级或 2 级楼梯,问到达楼顶有多少种不同的方式。

问题分析

  • 每次可以选择向上爬 1 层楼梯,或者 2 层楼梯。

  • 假设我们在第 n 层楼梯,那么在到达第 n 层楼梯时,前一步可以是从第 n-1 层楼梯爬上来的,或者从第 n-2 层楼梯跳上来的,从而得出下面的公式

f(n)=f(n−1)+f(n−2)

假设要计算 f(5)(到达 5 层楼梯的方式数):

  • f(5) = f(4) + f(3)

  • f(4) = f(3) + f(2)

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

  • f(2) = 2

  • f(1) = 1

边界条件

  • f(0) = 1:站在地面上,不需要爬任何楼梯,只有1种方式——什么都不做。

  • f(1) = 1:只有1层楼梯时,只能爬1步——1种方式。

  • f(2) = 2:有两层楼梯时,可以选择1次爬两层,或者2次、每次爬1层——两种方式。

代码实现

 简单递归法:

function climbStairs(n) {
    // 基本情况
    if (n === 0) return 0;
    if (n === 1) return 1;
    if (n === 2) return 2;
    // 递归
    return climbStairs(n - 1) + climbStairs(n - 2);
}

递归存在的问题:

重复计算许多相同的子问题,效率较低。例如,climbStairs(5) 会分别调用 climbStairs(4)climbStairs(3),而 climbStairs(4)climbStairs(3) 又会分别调用 climbStairs(3)climbStairs(2),以此类推,造成重复计算。

 

改进的递归实现

为了避免重复计算,可以使用 记忆化(Memoization)技巧,将已经计算过的结果存储起来,下次需要时直接返回,避免重复计算,从而将时间复杂度降低到 O(n)

 1 function climbStairs(n) {
 2     // 使用一个对象存储已经计算过的结果
 3     const memo = {};
 4     
 5     function helper(n) {
 6         // 如果已经计算过,直接返回结果
 7         if (n === 0) return 0;
 8         if (n === 1) return 1;
 9         if (n === 2) return 2;
10         
11         // 如果之前没有计算过,进行递归并缓存结果
12         if (memo[n] === undefined) {
13             memo[n] = helper(n - 1) + helper(n - 2);
14         }
15         
16         return memo[n];
17     }
18     
19     return helper(n);
20 }

 

posted @ 2025-03-26 11:38  我是格鲁特  阅读(82)  评论(0)    收藏  举报