爬楼梯
爬楼梯问题是一个经典的动态规划问题,通常表述为:一个人站在楼梯的底部,每次可以选择爬 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 }

浙公网安备 33010602011771号