双向广搜,顾名思义就从两个地方开始广搜
当然,这两个地方一定一个是起点状态,一个是终点状态
目录
算法引入
还是先来看一张图
左边这张图可以大概成单方面的,而右边是双向
我们可以看出,蓝色的面积比黄色的小很多
所以这就是双向的好的地方
更省时,也更省空间
但是正如前言所说,双向也是有条件限制的
起点和终点状态必须都知道
算法思路
就是普通的从两个方向开始就行了
伪代码
经典板子
void bfs_expand(queue<Status> &q,bool flag) {//拓展
s=q.front();
q.pop();
for(每个s个儿子t) {
t.state=gethash(t.temp);//每个子节点特有的状态
if(flag)//正向队列
if(vis[t.state]!=1) {//没被搜过
if(vis[t.state]==2) {//反向队列已经找到了
按题目要求的操作//有结果了
found=1;
return;
}
vis[t.state]=1;
q.push(t);
}
else//反向队列
if(vis[t.state]!=1) {//没被搜过
if(vis[t.state]==2) {//正向队列已经找到了
按题目要求的操作//有结果了
found=1;
return;
}
vis[t.state]=1;
q.push(t);
}
}
}
void two_way_bfs() {//双向BFS
if(s1.state==s2.state) {//起点终点相同,特判
found=1;//标记
return;
}
found=0;
memset(vis,0,sizeof(vis));//记得清零
while(!q1.empty()) q1.pop();//正向队列
while(!q2.empty()) q2.pop();//反向队列
vis[s1.state]=1;//1表示正向找到的
vis[s2.state]=2;//2表示反向找到的
q1.push(s1);//起点为正向
q2.push(s2);//终点为反向
while(!q1.empty()||!q2.empty()) {
if(!q1.empty())
bfs_expand(q1,1);//正向搜
if(found)//已有结果
return;
if(!q2.empty())
bfs_expand(q2,0);//反向搜
if(found)//已有结果
return;
}
}
但是,这个板是有问题的
反例
如图::
从1开始,从7结束
队列会这样的::
为了区分和
,这里用中括号代表
,大括号代表
所以我们就可以看到,先搜到的点是3号点
所以最短路就是
但这显然不是最短路
所以我们换一种做法,只用1个队列来存
再来模拟一下::
我们就可以得到正确的路了
正确的板子
while(!queue.empty()) {
t=queue.front();
queue.pop();
for(每个t的儿子s) {
if(正向搜索)
if(vis1[s]==0) {//找到答案
if(vis2[s]==1)
over;
vis1[s]=1;
queue.push(s);
}
else//反向搜索
if(vis2[s]==0) {//找到答案
if(vis1[s]==1)
over;
vis2[s]=1;
queue.push(s);
}
}
}
算法分析
时间复杂度和空间复杂度均优于普通的,代码也比较好打,好用
例题
现场施工中