学校科技节办了一场密室逃脱:密室被划分成 9 个格子,其中有 8 个格子上摆着编号 1~8 的滑块,剩下 1 个是空格。队伍要做的,是把滑块推到"123 / 456 / 780"这个目标摆法,步数最少才能拿到奖励。
第一队玩得很"老实":从初始局面开始,一层一层地把"走 1 步能到的局面""走 2 步能到的局面"全部列出来,像水波一样向外扩散,遇到目标就停 —— 这就是 广度优先搜索(BFS)。可他们很快发现:每推一步,新出现的局面就多好几倍,走到第 15 层时,桌面上的卡片已经堆成小山(约 108 张)。
第二队换了个思路。他们拿了一份导航软件式的评分表:每个局面都算一个分数 f = 已经走的步数 g + 离目标还有多远的估计 h。每次只推进"分数最小"的那个局面。因为这个估计 h 总是往小了说、从不夸大,所以他们第一次摸到目标时,走的就是最短路线 —— 这就是 A* 启发式搜索。
第三队更狡猾。他们派出两个人:一个从起点往外推,一个从目标倒着往外推,谁的"波前"小就先扩谁。两边波前在中间一碰面,答案立刻合并出来。原本要 bd 次的搜索,变成了 2·bd/2 次 —— 这就是 双向广度优先搜索。
第四队带了钓鱼线:先只允许"最多走 1 步",找不到就放到"最多 2 步",再不行放到"最多 3 步"……每层都用 DFS 深度受限地搜。DFS 只占 O(d) 的栈空间,而重复搜的那些浅层又少得可怜(总共才多花一倍左右)。这就是 迭代加深搜索;如果每次加深时还顺手用 A* 的 f 值剪掉明显不可能的分支,那就是 IDA*。
三把钥匙,其实解决的是同一个问题:指数爆炸下如何把搜索规模压下来。
💡 故事背后的算法思想
我们面对的是典型的"隐式图最短路"问题:状态数量太大,图无法显式存储,只能"边搜边生成"。朴素的 BFS 花费 O(bd) 时间与空间,朴素的 DFS 空间只有 O(d) 但找到的解不一定最浅。
本讲的三种技术分别从三个维度削弱指数:A* 削减每层的分支(只扩展有希望的状态),双向 BFS 削减指数本身(把 d 变成 d/2),迭代加深削减空间(时间只多常数倍,空间从 bd 降到 d)。
一句话:能估就估、能对搜就对搜、能省内存就逐层放宽。