返回

LeetCode JavaScrpt 爬楼梯:三种思路手把手带你入门

前端

LeetCode 爬楼梯:三种思路手把手带你入门

引言

LeetCode 上的「爬楼梯」问题可谓是经典中的经典,解题思路高达5k+,足见其受欢迎程度。无论是在面试还是笔试中,这道题的出现频率都非常高。作为一道数学本质为「斐波那契数列」的问题,「爬楼梯」看似简单,实则蕴含着深奥的算法原理。本文将从基础概念入手,循序渐进地介绍求解此问题的三种思路:动态规划、递归和迭代,并辅以清晰的代码示例和详细的讲解,帮助读者深入理解算法的原理和实现,为面试和笔试做好充分准备。

斐波那契数列

斐波那契数列是一个以0和1为起始项,且每一项等于前两项之和的数列。其通项公式为:

F(n) = F(n-1) + F(n-2)

其中:

  • F(n) 表示第 n 项
  • F(n-1) 表示第 n-1 项
  • F(n-2) 表示第 n-2 项

斐波那契数列在自然界和数学中有着广泛的应用,如生物生长、股市波动和密码学等。

爬楼梯问题

「爬楼梯」问题的如下:

假设你正在爬楼梯,每次可以爬一步或两步。给你一个需要爬的阶梯数 n,返回到达顶部有多少种不同的方法。

三种求解思路

1. 动态规划

动态规划是一种自底向上的解题方法,它将问题分解为一系列子问题,逐步求解,并存储子问题的解,避免重复计算。对于「爬楼梯」问题,我们可以定义状态转移方程:

dp[n] = dp[n-1] + dp[n-2]

其中:

  • dp[n] 表示爬 n 阶楼梯的方法数
  • dp[n-1] 表示爬 n-1 阶楼梯的方法数
  • dp[n-2] 表示爬 n-2 阶楼梯的方法数

代码示例:

function climbStairsDP(n) {
  if (n <= 2) {
    return n;
  }
  const dp = new Array(n + 1).fill(0);
  dp[1] = 1;
  dp[2] = 2;
  for (let i = 3; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
}

2. 递归

递归是一种自顶向下的解题方法,它将问题分解为更小的子问题,并调用自身来解决这些子问题。对于「爬楼梯」问题,我们可以定义递归函数:

function climbStairsRecursive(n) {
  if (n <= 2) {
    return n;
  }
  return climbStairsRecursive(n - 1) + climbStairsRecursive(n - 2);
}

代码示例:

function climbStairsRecursive(n) {
  if (n <= 2) {
    return n;
  }
  return climbStairsRecursive(n - 1) + climbStairsRecursive(n - 2);
}

3. 迭代

迭代是一种循环的解题方法,它逐次执行操作,直到达到终止条件。对于「爬楼梯」问题,我们可以定义迭代变量:

let oneStep = 1;
let twoSteps = 2;

然后循环迭代,直到达到 n 阶楼梯:

for (let i = 3; i <= n; i++) {
  const tmp = oneStep;
  oneStep = oneStep + twoSteps;
  twoSteps = tmp;
}

代码示例:

function climbStairsIterative(n) {
  if (n <= 2) {
    return n;
  }
  let oneStep = 1;
  let twoSteps = 2;
  for (let i = 3; i <= n; i++) {
    const tmp = oneStep;
    oneStep = oneStep + twoSteps;
    twoSteps = tmp;
  }
  return oneStep;
}

总结

动态规划、递归和迭代是求解「爬楼梯」问题的三种常见思路。每种思路都有其独特的优缺点:

  • 动态规划 :空间复杂度较低,但时间复杂度较高。
  • 递归 :容易理解,但空间复杂度较高,易出现「栈溢出」问题。
  • 迭代 :空间复杂度较低,容易理解,但代码可能不那么直观。

在实际应用中,应根据具体情况选择最合适的解题思路。通过本文的详细讲解和代码示例,相信读者已经对这三种思路有了深入的理解。在面试和笔试中,熟练掌握这些思路将成为你脱颖而出的关键。