✨ 一篇让小可爱都能看懂的回溯算法讲解
🍹 用“奶茶铺点单”的生活例子彻底理解递归 + 回溯思维!
(带题:LeetCode-Hot100 46《全排列》)
💬 一、回溯法到底是干嘛的?
一句话解释:
回溯法就是“试错 + 撤销”的暴力搜索。
当我们不知道哪条路对的时候,就:
👉 先走一步,
👉 不对就退回来,
👉 再换一条路继续试。
就像在一座迷宫里找出口:
🚶♀️ 走 → 撞墙 → 回头 → 换条路 → 继续走
这,就是“回溯”的核心精神。
🍹 二、生活比喻版:奶茶铺点单法
想象你在一家奶茶店打工,顾客说:
“请列出我能点的所有奶茶组合,
每种口味只能用一次,比如珍珠、红豆、椰果。”
于是你开始了“回溯式点单”👇
🧋 Step 1. 先选一杯
顾客可能先选珍珠奶茶。
path = [珍珠]
🧋 Step 2. 再选下一杯
还没点完,再选红豆奶茶。
path = [珍珠, 红豆]
🧋 Step 3. 继续加
选椰果奶茶。
path = [珍珠, 红豆, 椰果]
✅ 菜单选完,保存这单!
🧋 Step 4. 撤销
“好,我再换一种组合看看~”
把最后一个口味“椰果”撤掉。
path = [珍珠, 红豆]
再试别的顺序,比如红豆 → 椰果互换。
如此反复,直到所有组合都试完。
💡 三、题目背景:LeetCode 46. 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。
你可以 按任意顺序 返回答案。
示例:
输入:nums = [1,2,3]
输出:[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
🧋 四、题目翻译成奶茶店语言
你有三种配料:
A = 珍珠
B = 红豆
C = 椰果
顾客说:“请你列出所有调配顺序,比如:
- 珍珠→红豆→椰果
- 红豆→椰果→珍珠
- 椰果→珍珠→红豆 …
这就是「全排列」的本质:
列出所有不重复的加料顺序。
💻 五、代码实现(完整版 + 注释)
var permute = function(nums) {
const res = []; // 存放结果的“托盘”
const path = []; // 当前正在调配的“杯子”
const used = Array(nums.length).fill(false); // 哪些料加过
function backtrack() {
// 🍶 杯子满了,就打包保存
if (path.length === nums.length) {
res.push([...path]);
return;
}
// 🧋 遍历每一种配料
for (let i = 0; i < nums.length; i++) {
if (used[i]) continue; // 已加过就跳过
// 🥤 做选择:加一份配料
path.push(nums[i]);
used[i] = true;
// 🍹 递归:继续往下加下一份料
backtrack();
// 🔙 撤销选择:倒掉刚加的料
path.pop();
used[i] = false;
}
}
backtrack(); // 从空杯子开始试
return res; // 返回所有排列(所有奶茶组合)
};
“开始调奶茶!等全部组合完成后,把托盘端出来给顾客。”
🧠 六、运行过程演示(nums = [1,2,3])
start []
├── [1] → 继续选 → [1,2] → [1,2,3]
│ ↳ 回退 → [1,3,2]
├── [2] → [2,1,3], [2,3,1]
└── [3] → [3,1,2], [3,2,1]
每次深入是递归(往下一层点单)
每次回退是回溯(撤销上一步)
| 调用层级 | 当前 path | 操作说明 |
|---|---|---|
| ① | [] | 顾客刚来,还没选 |
| ② | [1] | 选了珍珠 |
| ③ | [1,2] | 接着选红豆 |
| ④ | [1,2,3] ✅ | 满杯!保存结果 |
| ← 回溯 | [1,2] | 拿掉3换别的 |
| ③ | [1,3] | 换椰果 |
| ④ | [1,3,2] ✅ | 保存结果 |
| ← 回溯 | [1] | 撤销,换起手配料 |
| ② | [2] | 从红豆开始 |
| … | … | 直到列完所有顺序 |
🍰 七、记忆法总结(小可爱专属 🤓)
回溯三部曲:
🥤「选择」 → 选一种配料放入杯子
🧭「探索」 → 递归往下继续加
🔙「撤销」 → 拿掉最后加的料,换别的试
一句话总结:
“一边加料一边记录,做完一杯就回去换口味!” 😆
🎯 八、最终成果
console.log(permute([1,2,3]));
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
💬 九、最后总结
🍵 回溯法其实就是「带撤销功能的递归」。
每次做选择 → 尝试 → 回退 → 换新路。
适合场景:
- 全排列 / 组合 / 子集
- 数独求解
- 八皇后问题
- 迷宫路径
🧋一句话收尾:
“回溯,就是奶茶小哥穷举所有加料顺序,
每次尝完一种,再回去换新口味!” 🍓