关于双向BFS的一点学习
双搜有一个不短的英文名Bi-Directional Breadth-First-Search(双向广度优先搜索)
先来说一下什么是搜索吧。根据我的理解,搜索就是根据某种扩展规则,从某一个(连通图)或几个(非连通图)点(或状态)开始,遍历所有可能达到的点(或状态),简单来说就是遍历所有状态,找出其中的可行解(或最优解)。有很多问题像一个事物可以变化出很多状态,所求涉及到起止状态间的线路、步数、可达性等,用搜索来做就比较合适了。
当然还是要用队列,不过原先的一个已经不够了,双搜要用两个,开始时要把两个搜索起点first1和first2分别放入队列,然后向普通BFS一样开始搜索就行了;循环结束条件可以不变,因为如果其中一个队列跑完了还没有出结果,那就是没有路了;visit数组不用变,但用法有点不同了,不能再用bool型,可以用short、char之类的,为什么这样呢?因为仅“走过”和“没走过”已经不能满足双搜的visit需求了,双搜的visit有三种状态,分别是:①队列1走过、②队列2走过、③没走过;循环里的内容要扩增一倍,进入循环后先把一个起点扩增一层,再去扩另一个起点,此重循环结束;按照先前的想法,碰头时结束,返回此点到两起点的距离和,判断碰头的方法就是队1走到队2的地牌上或反过来;还有最后一点要注意,如果据题意建出的图是无向图,那么两边扩展方法就应该是一样的,但如果是有向图,起点要正着扩展,终点则要反着。差不多就这些了。
听过来人说,搜索部分的最重难点在剪枝(无穷无尽神鬼难测想破脑袋也想不到的剪枝啊,给跪……)比如奇偶剪枝、迭代加深、xxxx、……,双搜也有一个比较固定的剪枝方法,我管它叫“平衡队列”,提供速度的关键在于使状态扩展得少一些,所以优先选择队列长度较少的去扩展,保持两边队列长度平衡。这比较适合于两边的扩展情况不同时,一边扩展得快,一边扩展得慢。
下面来说一说为什么双搜比单搜快。我们不妨假设每次搜索的分支因子是r,如果最短的路径长为L的话(也就是搜了L层),那么,用一般的BFS算法(不考虑去掉重复状态),总的搜索状态数是r^L(^表示乘方运算);而如果采取双向BFS算法,那么,从前往后搜,我们只需要搜索L/2层,从后往前搜,我们也只要搜L/2层,因此,搜索状态数是2*(r^(L/2)),比普通BFS就快了很多了。
参考资料:
【1】http://www.cppblog.com/Yuan/archive/2011/02/23/140553.aspx
【2】http://blog.sina.com.cn/s/blog_6635898a0100p4wd.html
PS:搜资料时找到一篇很好的搜索算法图示比较,很详细,不过因为太过高端,没能引用的上。
http://www.2cto.com/kf/201104/87378.html