例8.1 城市A到城市B的交通图:
A->:b,c,d,f;
B->:a,f;
C->a,d,e;
D->a,d,g;
E->c,g,h;
F->a,b,h;
G->d,e,h;
H->e,f,g
从路线中可以看出A到H要经过若干个城市,现在要找出一条经过城市最少的路线
分析:
1.很容易想到用邻接矩阵来表示,0表示能走,1表示不能走
2.邻接矩阵最短路径想到用队列来表示
3.a数组是存储扩展节点的队列,a[i]记录经过的城市,b[i]记录前趋城市
4.将城市A入队,队首为0,队尾为1
5.将队首所指的城市所有可直通的城市入队(若出现过就不入队),将入地城市的前趋城市保存在b[i]中,
6.然后将队首加1,得到新的队首城市。重复以上步骤直到搜到H结束。
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int ju[9][9]{
{0,0,0,0,0,0,0,0,0},
{0,1,0,0,0,1,0,1,1},//a和a之间是1,是不能走的
{0,0,1,1,1,1,0,1,1},
{0,0,1,1,0,0,1,1,1},
{0,0,1,0,1,1,1,0,1},
{0,1,1,0,1,1,1,0,0},
{0,0,0,1,1,1,1,1,0},
{0,1,1,1,0,0,1,1,0},
{0,1,1,1,1,0,0,0,1}
};
int a[101],b[101];
bool s[9];
//输出过程
void out(int d){
cout<<char(a[d]+64);
while(b[d]){
d=b[d];
cout<<"--"<<char(a[d]+64);
}
cout<<endl;
}
//BFS算法实现
void doit(){
//数据初始化
int head,tail,i;
head=0;//队首为0
tail=1;//队尾为1,因为a到a不能算
a[1]=1;//记录经过的城市,现在第一个经过的是a本身
b[1]=0;//记录前趋城市,a前面还没有城市
s[1]=1;//表示该城市已经到过,现在已经到过a
//循环判断
do{
head++;//队首加1,出队,从上一个城市往后走,第一次是从a开始
for(i=1;i<=8;i++){//搜索可直达的城市
if((ju[a[head]][i]==0&&(s[i]==0))){//下个城市能直达并且没被走过,就是可走
tail++;//队尾加1
a[tail]=i;//将i入队
b[tail]=head;//记录tail的前趋城市
s[i]=1;//记录该城市已经走过
if(i==8){//第一次搜到的H城市最短
out(tail);
head=tail;
break;
}
}
}
}while(head<tail);
}
//主函数
int main(){
memset(s, false, sizeof(s));
doit();
return 0;
}
运行结果:
总结:
广度优先搜索的核心思想
实际上是一个先进先出的队列(FIFO)
从初始节点开始,应用算符生成第一层节点,检查目标节点是否在这些后继节点中,
若没有,再用产生式规则将第一层的节点逐一扩展,得到第二层节点,并逐一检查第二层节点中是否包含目标节点,
若没有,再用算符逐一扩展到第二层的所有节点···
如此依次扩展,检查下去,直到发现目标节点为止
补充:
刚开始有点不太懂,即使懂了思想但是代码也不太理解,
但是画过图之后就比较容易理解了,这里以邻接链表为例
(参考青岛大学王卓老师的视频:https://www.bilibili.com/video/av36337654?from=search&seid=17495355141589125432)
初始时:
先访问v1,他有两个子节点,所以将v2,v3入队
(类似于邻接矩阵中的
if((ju[a[head]][i]==0&&(s[i]==0))){//下个城市能直达并且没被走过,就是可走
tail++;//队尾加1
a[tail]=i;//将i入队
b[tail]=head;//记录tail的前趋城市
s[i]=1;//记录该城市已经走过)
然后从v2开始访问,它的三个子节点是v1,v4,v5(对应序号是0,3,4)
v1访问过了,所以只用将v4,v5入对就可以了
以此类推,后面就是从v3开始,再将v6,v7入队
后面就是依次入队,出队..................