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

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

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

相关推荐
两个西柚呀1 小时前
es6和commonjs模块化规范的深入理解
前端·javascript·es6
爱吃大芒果1 小时前
Flutter 表单开发实战:表单验证、输入格式化与提交处理
开发语言·javascript·flutter·华为·harmonyos
光影少年1 小时前
RN vs Flutter vs Expo 选型
前端·flutter·react native
风止何安啊2 小时前
🚀别再卷 Redux 了!Zustand 才是 React 状态管理的躺平神器
前端·react.js·面试
白露与泡影2 小时前
2026年Java面试题目收集整理归纳(持续更新)
java·开发语言·面试
鹿角片ljp2 小时前
Spring Boot Web入门:从零开始构建web程序
前端·spring boot·后端
向下的大树2 小时前
Vue 2迁移Vue 3实战:从痛点到突破
前端·javascript·vue.js
我很苦涩的2 小时前
原生小程序使用echarts
前端·小程序·echarts
玉米Yvmi2 小时前
从零理解 CSS 弹性布局:轻松掌控页面元素排布
前端·javascript·css