小明的魔法地图:迷宫探险智慧(记忆性递归)

🌲 故事:小明的迷宫探险与“魔法地图”

🌍 背景:神秘的数字迷宫

小明来到一座古老的迷宫。迷宫有 n 个房间,从房间 0 开始,每次可以:

  • 走到房间 n-1(走1步)

  • 或直接跳到房间 n-2(走2步)

他的任务是:从房间 n 走到房间 0,有多少种不同的路径?

这其实就是 爬楼梯问题,但我们现在把它想象成一个图上的搜索问题


🧭 第一章:深度优先搜索(DFS)—— 小明的盲目探索

小明决定用 深度优先搜索(DFS) 来探索所有路径。

他从房间 5 出发,开始“递”:

 f(5)
 ├── f(4)
 │   ├── f(3)
 │   │   ├── f(2)
 │   │   │   ├── f(1) → f(0) ✅(一条路径)
 │   │   │   └── f(0) ✅
 │   │   └── f(1) → f(0) ✅
 │   └── f(2) → 同上 ✅
 └── f(3) → 又从头算一遍!😱

👉 他发现:f(3) 被算了 两次f(2) 被算了 三次……

他累得满头大汗:“这迷宫太深了,我一直在走重复的路!”

❌ 问题:纯 DFS 会重复探索相同的子问题,效率极低,时间复杂度 O(2^n)。


🗺 第二章:魔法地图登场 —— 记忆化递归

一位智者出现,递给小明一张 “魔法地图”memo):

“孩子,每次你探索完一个房间,就把从那里到终点的路径总数记在地图上。下次再来,就不用再探了。”

小明恍然大悟!

🧩 魔法地图规则(记忆化递归)

class Solution {
 private:
     unordered_map<int, int> memo;  // 魔法地图:记录 f(n) = 从房间 n 到 0 的路径数
 ​
 public:
     int dfs(int n) {
         // 🔍 第一步:进入房间 n,先看地图!
         if (memo.find(n) != memo.end()) {
             return memo[n];  // ✅ 地图有记录,直接返回!不再“递”下去!
         }
 ​
         // 🧱 基础情况:房间 1 和 2
         if (n == 1) return 1;
         if (n == 2) return 2;
 ​
         // 🚶•♂️ 第一次来:开始“递”——深入探索 f(n-1) 和 f(n-2)
         int ways = dfs(n - 1) + dfs(n - 2);
 ​
         // 📝 探索结束,在“归”的路上,把结果记入地图
         memo[n] = ways;
 ​
         // 🏁 返回结果
         return ways;
     }
 ​
     int climbStairs(int n) {
         return dfs(n);
     }
 };

🎬 第三章:魔法地图如何工作?—— “递” vs “归”

我们以 f(5) 为例,看看魔法地图何时“记录”,何时“优化”。

🌦 第一次探索 f(5):建立地图

 f(5)
 ├── f(4)
 │   ├── f(3)
 │   │   ├── f(2) → 2
 │   │   └── f(1) → 1
 │   │   → f(3) = 3,归时记入 memo[3]=3 ✅(归:记录)
 │   └── f(2) → 2
 │   → f(4)=5,归时记入 memo[4]=5 ✅(归:记录)
 └── f(3) → 还没记完,继续

👉 在“”的过程中,小明把 f(3)f(4) 的结果写入了魔法地图。


☀️ 第二次遇到 f(3):触发优化!

f(5) 要调用 f(3) 时:

 if (memo.find(3) != memo.end()) return memo[3];  // ✅ 查表成功!
  • 这个判断发生在 “递”的最开始

  • 小明甚至不用走进房间 3,直接说:“我知道,有 3 种走法!”

  • 整个 f(3) 子树被跳过!

优化发生在这里:在“递”的过程中,查表成功,跳过递归!


🌟 关键结论:优化到底在“递”还是“归”?

阶段发生了什么是否优化?
第一次 f(3) 的“归”f(3)=3 写入 memo❌ 准备工作(无优化)
第二次 f(3) 的“递”查表成功,直接返回优化真正发生!

📌 所以:

  • “归”是播种:把结果记下来,为未来优化做准备。

  • “递”是收获:在后续的“递”中,利用记忆避免重复探索。

  • 优化的本质效果,发生在“递”的过程中。


🧠 深度优先搜索(DFS)与记忆化的关系

概念对应故事中的角色
DFS(深度优先搜索)小明一条路走到黑,深入迷宫
递归小明不断进入新房间,靠“记忆”返回
函数调用栈小明的“记忆栈”:记得自己是从哪个房间来的
记忆化(memo)魔法地图:记录已探索房间的结果
优化时机在“递”时查地图,避免重复进入

记忆化递归 = DFS + 记忆表 它让 DFS 从“盲目探索”变成“聪明搜索”。


📊 效果对比

n纯 DFS(递归)调用次数记忆化 DFS 调用次数
515 次9 次
10~200 次19 次
40~1万亿次 ❌79 次 ✅

小明从“累死也走不完”变成了“秒答如流”。


✅ 总结:记忆化递归的三大智慧

  1. “递时查表”:每次进入函数,先查 memo,避免重复递归。

  2. “归时记录”:计算完结果后,在返回前存入 memo

  3. “用空间换时间”:多用一点内存(memo),换来指数级的速度提升。


🚀 最终代码(带故事注释)

 class Solution {
 private:
     unordered_map<int, int> memo;  // 🗺 魔法地图:记录每个房间到终点的路径数
 ​
 public:
     int dfs(int n) {
         // 🔍 【优化发生在这里!】
         // 进入房间 n,先看地图:如果已经探索过,直接返回答案
         if (memo.find(n) != memo.end()) {
             return memo[n];  // ✅ 跳过整个子树,不再“递”下去
         }
 ​
         // 🧱 基础情况:房间 1 和 2
         if (n == 1) return 1;
         if (n == 2) return 2;
 ​
         // 🚶•♂️ 第一次来:开始“递”——深入探索子房间
         int ways = dfs(n - 1) + dfs(n - 2);
 ​
         // 📝 【归时记录】探索结束,在返回前把结果记入地图
         memo[n] = ways;
 ​
         // 🏁 返回结果
         return ways;
     }
 ​
     int climbStairs(int n) {
         return dfs(n);
     }
 };

🌈 故事的启示

递归是深度优先搜索的实现方式,而记忆化是给 DFS 装上“记忆”和“智慧”。

小明学会了:

  • 归时记一笔,递时省一步

  • 不走重复路,才是真高手

你也已经掌握了这门“魔法”,恭喜!🧙‍♂️✨


如果你还想看“回溯”、“动态规划”等更多故事版本,我也可以继续讲下去 😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值