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

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

一道看似"幼儿园难度"的面试题:
"每次能爬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 的分水岭。

相关推荐
鬼手点金3 小时前
nanoGPT/nanochat 能做出 DeepSeek/Qwen 这样的大模型吗?
服务器·前端·javascript·bug·openclaw
软件技术NINI3 小时前
美妆商城html+Css+js 4页
javascript·css·html
蛋蛋的就会顺顺的3 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
程序员Sunday3 小时前
TypeScript 的 any、unknown、never 有什么区别?
前端·面试·typescript
朱涛的自习室4 小时前
70 美金的 Beyond Compare,我让 Gemini 4 和 DeepSeek 造个开源免费版(Day 2)
android·前端·人工智能
小 黄 鸡4 小时前
HashMap 面试必会知识点详解(JDK 8)
java·面试·职场和发展
码艺-Alimjan4 小时前
Tauri 源码备份 · 打包 & 解压 总结,源码收集之路
前端·javascript·vue.js
小马不起床4 小时前
零基础玩转 FastAPI_11:前端后台管理页面
前端·状态模式·fastapi
子非鱼a6 小时前
【文件上传】一个普普通通的edu16
前端
美狐美颜SDK开放平台6 小时前
直播APP如何实现美白滤镜功能?视频美颜SDK技术开发方案详解
前端·数据库·人工智能·音视频·美颜sdk·直播美颜sdk·视频美颜sdk