广度优先搜索

搜索算法的实现,从树的遍历角度讲,有深度优先和广度优先两种。深度优先我们在前边已经介绍过,我们先来简单了解一下:
     如算法名称那样,深度优先搜索所遵循的搜索策略是尽可能“深”地搜索树。在深度优先搜索中,对于当前发现的结点,如果它还存在以此结点为起点而未探测到的边,就沿此边继续搜索下去,若当结点的所有边都己被探寻过.将回溯到当前结点的父结点,继续上述的搜索过程直到所有结点都被探寻为止。
    深度优先搜索在树的遍历中也称作树的先序遍历。对于树而言,深度优先搜索的思路可以描述为:
    (1)将根结点置为出发结点。
    (2)访问该出发结点.
    (3)依次将出发结点的子结点置为新的出发结点.进行深度优先遍历(执行(2))。
    (4)退回上一层的出发结点。

深度优先搜索图示(以八数码问题为例)

树的广度优先遍历 java实现 树的广度优先搜索_树的广度优先遍历 java实现

广度优先搜索的过程

广度优先搜索算法(又称宽度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。

        广度优先算法的核心思想是:从初始节点开始,应用算符生成第一层节点,检查目标节点是否在这些后继节点中,若没有,再用产生式规则将所有第一层的节点逐一扩展,得到第二层节点,并逐一检查第二层节点中是否包含目标节点。若没有,再用算符逐一扩展第二层的所有节点……,如此依次扩展,检查下去,直到发现目标节点为止。即

       ⒈从图中的某一顶点V0开始,先访问V0;

       ⒉访问所有与V0相邻接的顶点V1,V2,......,Vt;

       ⒊依次访问与V1,V2,......,Vt相邻接的所有未曾访问过的顶点;

       ⒋循此以往,直至所有的顶点都被访问过为止。

       这种搜索的次序体现沿层次向横向扩长的趋势,所以称之为广度优先搜索。

树的广度优先遍历 java实现 树的广度优先搜索_广度优先搜索_02

广度优先搜索算法描述1:

树的广度优先遍历 java实现 树的广度优先搜索_广度优先搜索_03

1 Program Bfs;
 2 初始化,初始状态存入队列;
 3 队列首指针head:=0; 尾指针tail:=1;
 4 repeat
 5     指针head后移一位,指向待扩展结点;
 6     for I=1 to max do        //max为产生子结点的规则数
 7        if 子结点符合条件 then
 8           if 新结点与原已产生结点不重复 then
 9               begin
10                  tail指针增1,把新结点存入列尾;
11                  if新结点是目标结点 then 输出并退出;
12               end;
13 until(head>=tail);            //队列为空

树的广度优先遍历 java实现 树的广度优先搜索_广度优先搜索_03

广度优先搜索算法描述2:

树的广度优先遍历 java实现 树的广度优先搜索_广度优先搜索_03

1 Program Bfs; 
 2 初始化,初始状态存入队列; 
 3 队列首指针head:=0; 尾指针tail:=1;
 4 repeat 
 5     指针head后移一位,指向待扩展结点; 
 6     for I=1 to max do        //max为产生子结点的规则数
 7        if 子结点符合条件 then 
 8     if 新结点是目标结点 then 输出
 9      else
10       if新结点与原已产生结点不重复 then 
11         tail 指针增1,把新结点存入队尾; 
12 until(head>=tail);   {队列空}

树的广度优先遍历 java实现 树的广度优先搜索_广度优先搜索_03

广度优先搜索注意事项:

        1、每生成一个子结点,就要提供指向它们父亲结点的指针。当解出现时候,通过逆向跟踪,找到从根结点到目标结点的一条路径。当然不要求输出路径,就没必要记父亲。
        2、生成的结点要与前面所有已经产生结点比较,以免出现重复结点,浪费时间,还有可能陷入死循环。
        3、如果目标结点的深度与“费用”(如:路径长度)成正比,那么,找到的第一个解即为最优解,这时,搜索速度比深度搜索要快些,在求最优解时往往采用广度优先搜索;如果结点的“费用”不与深度成正比时,第一次找到的解不一定是最优解。
        4、广度优先搜索的效率还有赖于目标结点所在位置情况,如果目标结点深度处于较深层时,需搜索的结点数基本上以指数增长。

广度优先搜索应用举例

【例1】图4表示的是从城市A到城市H的交通图。从图中可以看出,从城市A到城市H要经过若干个城市。现要找出一条经过城市最少的一条路线。

树的广度优先遍历 java实现 树的广度优先搜索_广度优先搜索_07

【算法分析】
看到这图很容易想到用邻接距阵来表示,0表示能走,1表示不能走。如图。

树的广度优先遍历 java实现 树的广度优先搜索_结点_08

 

首先想到的是用队列的思想。a数组是存储扩展结点的队列,a[i].city记录经过的城市,a[i].pre记录前趋城市,这样就可以倒推出最短线路。具体过程如下:

(1) 将城市A入队,队首为0、队尾为1。

(2) 将队首所指的城市所有可直通的城市入队(如果这个城市在队列中出现过就不入队,可用一个集合来判断),将入队城市的pre指向队首的位置。然后将队首加1,得到新的队首城市。重复以上步骤,直到搜到城市H时,搜索结束。利用pre可倒推出最少城市线路。

 

【程序参考课本303页理解】

广度优先搜索的算法框架

void bfs(){
head=0; tail=1;
q[tail]=初始状态; {初始状态进入队列}
while (head<tail){ {如果队列非空,则移出队首状态,扩展它所有的子状态}
head++; {移出队首元素}
expand(); {扩展它所有的子状态,满足条件的全部入队}
if (到达目标状态) 输出;
}
}

使用循环队列优化bfs空间
        在前面我们也发现了一些问题,随着不断在队首添加元素,队尾删除元素,队列中删除了的元素实际上没有真正删除,一直在占用空间,从而引起很大的浪费。那么我们能不能够循环使用这些空间呢?我们可以使用循环队列。
    上面的程序稍稍修改红色的部分即可实现(修改3处地方):
C++语言描述如下:

【例题2】 邮递员(laoj1375)
         有一个邮递员要在n个城市之间来回送信。但有的城市之间有大路相连而有的没有路。现在要由一个城市到另一个城市送信,中途最少要经过多少个其它的城市呢?
输入格式
         第一行是n,k(1<=n<=100, 1<=k<=500),接下来就是k行。
         这k行每行有两个数a,b(1 <= a,b <= n),表示城市a和b之间有大路k行以后就是两个数p和q。
输出格式
         输出从城市p到城市q之间最少要经过的其它的城市的数目。
         如果p和q之间不连通则输出"No solution"
样例输入
         6 6
         1 4
         1 2
         2 3
         3 4
         5 4
         5 6
         1 6
样例输出
         2
bfs模板题:

树的广度优先遍历 java实现 树的广度优先搜索_树的广度优先遍历 java实现_09

树的广度优先遍历 java实现 树的广度优先搜索_广度优先搜索_10

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,k,x,y, far[200],m=0,s=0;
bool a[200][200], vis[200];
int print(int a){
if(a==0)    return s;
else    s++;
print(far[a]);
}

void bfs(){
int q[11000], head=0,tail=1;
q[tail]=x;
far[x]=0;
vis[x]=1;
while(head <tail){
head++;
for(int i=1;i <=n;++i){
if(a[q[head]][i]==1 && vis[i]==0){
tail++;
q[tail]=i;
far[i]=q[head];
vis[i]=1;
if(q[tail]==y){
cout << print(q[tail])-2 << endl;
m=1;
return ;
}
}
}
}
}

int main(){ 
memset(a,0,sizeof(a));
memset(vis,0,sizeof(vis));
memset(far,0,sizeof(far));
int b,c;
cin >> n >> k;
for(int i=1;i <=k;++i){
cin >> b >> c;
a[b][c]=1;
a[c][b]=1;
}
cin >> x >> y;
bfs();
if(m==0) cout << "No solution" << endl;
}

View Code