爬楼梯?不,你在攀登算法的珠穆朗玛峰!

爬楼梯?不,你在攀登算法的珠穆朗玛峰!

一道看似"幼儿园难度"的面试题:
"每次能爬1阶或2阶,问爬到第n阶有几种方法?"

却暗藏递归、动态规划、记忆化、空间优化四大内功心法------

它不是考你会不会算数,而是看你有没有系统性思维


🧗‍♂️ 初见:天真递归 ------ "我能行!"(然后爆栈了)

最直觉的解法?当然是递归!

scss 复制代码
function climbStairs(n) {
  if (n === 1) return 1;
  if (n === 2) return 2;
  return climbStairs(n - 1) + climbStairs(n - 2);
}

逻辑完美

  • 要到第 n 阶,要么从 n-1 上来,要么从 n-2 跳上来
  • 所以 f(n) = f(n-1) + f(n-2) ------ 这不就是斐波那契?

但问题来了:

当你调用 climbStairs(45),电脑会疯狂重复计算:

  • f(43) 被算两次
  • f(42) 被算三次
  • ......
    时间复杂度 O(2ⁿ) ------ 指数爆炸!

就像你让一个人背完整本字典来查一个词------可行,但荒谬。


🧠 进阶:记忆化递归 ------ "我记住了!"

既然重复计算是罪魁祸首,那就把算过的答案存起来

ini 复制代码
const memo = {};
function climbStairs(n) {
  if (n === 1) return 1;
  if (n === 2) return 2;
  if (memo[n]) return memo[n]; // ← 关键:查缓存!
  memo[n] = climbStairs(n - 1) + climbStairs(n - 2);
  return memo[n];
}

效果 :每个 f(k) 只算一次 → 时间复杂度 O(n)

思想空间换时间 ,典型的自顶向下动态规划(Top-down DP)

但有个小瑕疵:memo 是全局变量,容易被污染。


🔒 优雅封装:闭包 + 记忆化 ------ "我的缓存,外人别碰!"

闭包memo 私有化,打造一个"智能函数":

ini 复制代码
const climbStairs = (function() {
  const memo = {}; // ← 外部无法访问!
  return function climb(n) {
    if (n === 1) return 1;
    if (n === 2) return 2;
    if (memo[n]) return memo[n];
    memo[n] = climb(n - 1) + climb(n - 2);
    return memo[n];
  };
})();

优势

  • 多次调用共享缓存(越用越快)
  • 状态私有,安全可靠
  • 接口干净:用户只需 climbStairs(n)

这不是函数,这是一个会学习、有记忆、懂封装的智能体


🚀 终极优化:自底向上 + 滚动变量 ------ "我不需要递归!"

其实,我们根本不需要递归,也不需要存所有中间值!

观察规律:

scss 复制代码
f(1) = 1
f(2) = 2
f(3) = f(2) + f(1) = 3
f(4) = f(3) + f(2) = 5
...

只需要两个变量,就能滚动计算出结果:

ini 复制代码
function climbStairs(n) {
  if (n === 1) return 1;
  if (n === 2) return 2;
  
  let prevPrev = 1; // f(i-2)
  let prev = 2;     // f(i-1)
  
  for (let i = 3; i <= n; i++) {
    const current = prev + prevPrev; // f(i)
    prevPrev = prev;   // 滚动窗口
    prev = current;
  }
  
  return prev;
}

时间复杂度 :O(n)

空间复杂度 :O(1) ------ 极致优化!

无递归:避免调用栈溢出(n 很大时更安全)

这就是自底向上的动态规划(Bottom-up DP) ------ 从已知出发,一步步推导未知。


📊 四种解法对比

方法 时间复杂度 空间复杂度 是否递归 适用场景
暴力递归 O(2ⁿ) O(n) 教学演示
记忆化递归 O(n) O(n) 中等规模,逻辑清晰
闭包记忆化 O(n) O(n) 需要缓存复用
滚动变量 O(n) O(1) 生产环境首选

💡 面试加分回答

当面试官问这道题,你可以这样说:

"我会根据场景选择方案:

  • 如果是教学或快速原型,用记忆化递归,逻辑直观;
  • 如果是高性能生产环境,用滚动变量的迭代法,O(1) 空间且无栈溢出风险。
    此外,我还会考虑边界情况(如 n ≤ 0)、类型校验,以及是否需要支持'每次可爬1~k阶'的扩展。"

------瞬间从"会写代码"升级到"有工程思维"。


🌟 结语:小题大智慧

"爬楼梯"从来不是一道数学题,而是一面镜子:

  • 它照出你是否理解递归的本质
  • 它检验你是否掌握动态规划的思想
  • 它考验你能否在简洁、性能、可维护性之间做权衡

下次再有人说"这题太简单",你可以微笑回应:

"是啊,简单到能写出四种境界。"

而这,正是优秀工程师和普通 coder 的分水岭。

相关推荐
passerby606121 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了29 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅32 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte2 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc