1. 从“数岛屿”说起:一个看似简单却暗藏玄机的问题
想象一下,你拿到了一张古老的海图,上面用密密麻麻的‘1’和‘0’标记着陆地与海洋。你的任务很简单:数清楚这片海域里到底有多少座独立的岛屿。规则是,上下左右相邻的‘1’(陆地)属于同一座岛屿,斜对角不算。听起来是不是像小时候玩的“连点成图”游戏?我第一次接触这个问题时也是这么想的,觉得不就是遍历一遍网格,遇到‘1’就把它和邻居都标记一下嘛。但当我真正动手去实现,尤其是当网格尺寸膨胀到几百乘几百,甚至更大时,我才发现,不同的“数法”效率天差地别,这里面的门道,足够我们好好聊一聊。
最直观的解法,也是很多朋友第一时间会想到的,就是深度优先搜索(DFS)。就像你走进一个迷宫,选择一条路走到黑,直到碰壁再原路返回,尝试下一条岔路。在岛屿问题里,就是从一个‘1’出发,递归地向四个方向探索,把所有连着的‘1’都标记为访问过(比如改成‘0’),这样就“淹没”了一座岛。然后继续扫描网格,找下一个未被访问的‘1’,重复这个过程。原始文章里给出的解决方案正是这种DFS思路,代码简洁明了,对于学习递归和理解连通性问题非常友好。
但是,DFS真的就是最优解吗?在实际的算法竞赛或者处理大规模地图数据时,我们常常会思考:这种“一条道走到黑”的方式,在极端情况下(比如整个网格都是‘1’,一个巨大的岛屿)会不会导致递归调用栈特别深,甚至引发栈溢出?虽然现代编译器和系统栈空间都比较大,但这始终是一个潜在的风险点。更重要的是,从算法设计的思维层面,我们是否还有其他同样高效、甚至在某些场景下更具优势的探索方式?这就引出了我们今天要深入探讨的主角——分支限界法。别被这个名字吓到,它的核心思想之一“广度优先搜索(BFS)”,你可能早就用过了。我们将看到,用BFS的思想来“数岛屿”,不仅同样清晰,还能带来一些不一样的视角和优势。
2. 庖丁解牛:深度理解分支限界法的核心
在直接动手用分支限界法改造岛屿问题之前,我们得先把它到底是个啥给掰扯清楚。很多教材讲得比较理论化,咱们今天就用最“人话”的方式来理解。你可以把分支限界法想象成一种**“聪明且有条理的搜索策略”**。
想象你要在一个巨大的、结构像树一样的迷宫里找宝藏(问题的解)。这棵树上的每个分岔点(结点)都代表一个可能的决策。分支限界法怎么工作呢?它不像DFS那样闷头钻一个分支,而是一层一层、由近及远地探索。首先,它站在树根(起点),环顾四周,把所有直接能看到的、下一步可能走的分岔路(子结点)都记到一个小本本(活结点表)上。然后,它从小本本上按某种规则(比如谁先来的就先看谁,这就是队列式FIFO;或者谁看起来离宝藏更近就先看谁,这就是优先队列式)挑一个分岔点走过去。到达这个新点后,它再次环顾四周,把新的分岔路记到小本本上,同时,它会非常“精明”地判断:有些路一眼望去就是死胡同(不满足约束条件),或者明显绕远了(不可能成为最优解),它就直接扔掉,根本不会记下来。这个过程反复进行,直到找到宝藏或者小本本被翻完(所有可能的路都探索或排除了)。
这和DFS的“钻牛角尖”式搜索有本质区别。DFS是认准一条路,不到黄河心不死,回溯回来再试下一条。而分支限界法是“眼观六路,耳听八方”,同时维护着一个待探索的边界(Frontier),这个边界像水波纹一样一圈圈扩散开去。在岛屿问题里,这个“边界”就是当前正在探索的岛屿的边缘陆地格子。
那么,分支限界法常用的两种组织“小本本”的方式就很重要了:
- 队列式(FIFO):这就是标准的广度优先搜索(BFS)。先记下来的路先探索,保证搜索是按距离起点的层次推进的。在数岛屿时,它能保证我们从一个陆地开始,均匀地“铺开”标记整座岛屿。
- 优先队列式:给每条待探索的路估个“价”(优先级),总是先探索估价最有利的。这在寻找最短路径、最优解问题时非常有用。虽然在单纯的数岛屿问题上,我们不需要优先级,但理解这种思想对后续解决更复杂问题(比如找面积最大的岛屿)有帮助。
简单总结一下,分支限界法,特别是其队列式实现(BFS),为我们提


242

被折叠的 条评论
为什么被折叠?



