110 likes | 363 Views
广度优先搜索算法是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。如 Dijkstra 单源最短路径和 Prim 最小生成树算法都采用了广度优先搜索的思想。 核心思想:从初始节点开始,应用算符生成第一层节点,检查目标节点是否在这些后继节点中,若没有,再用产生式规则将所有第一层的节点逐一扩展,得到第二层节点,并逐一检查第二层节点中是否包含目标节点,若没有,再用算符逐一扩展第二层的所有节点 … ,如此依次扩展,检查下去,直到发现目标节点为止。即: 1 、从图中的某一顶点 v0 开始,先访问 v0 ; 2 、访问所有与 v0 相邻接的顶点 v1 、 v2… ;
E N D
广度优先搜索算法是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。如Dijkstra单源最短路径和Prim最小生成树算法都采用了广度优先搜索的思想。广度优先搜索算法是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。如Dijkstra单源最短路径和Prim最小生成树算法都采用了广度优先搜索的思想。 核心思想:从初始节点开始,应用算符生成第一层节点,检查目标节点是否在这些后继节点中,若没有,再用产生式规则将所有第一层的节点逐一扩展,得到第二层节点,并逐一检查第二层节点中是否包含目标节点,若没有,再用算符逐一扩展第二层的所有节点…,如此依次扩展,检查下去,直到发现目标节点为止。即: 1、从图中的某一顶点v0开始,先访问v0; 2、访问所有与v0相邻接的顶点v1、v2…; 3、依次访问与v1、v2…vt相邻接的所有未曾访问过的顶点; 4、循此以往,直到所有的顶点都被访问过为止。 这种搜索的次序体现了沿层次向横向扩展的趋势,所以称之为广度优先搜索。 第八章 广度优先搜索算法
【模块1】 Program bfs; 初始化,初始状态存入队列; 队列首指针head:=0; 尾指针tail:=1; while head<tail do begin inc(head);指针head后移一位,指向待扩展节点; for i:=1 to max do begin if 新节点是目标节点 then 输出并退出; if 新节点符合条件,并且新节点与原已产生的节点不重复 then tail指针加1,把新节点加入到队尾; end; end; 算法描述模块(运用了队列的结构) 【模块2】 Program bfs; 初始化,初始状态存入队列; 队列首指针head:=0; 尾指针tail:=1; repeat inc(head); 指针head后移一位,指向待扩展节点; for i:=1 to max do begin if 新节点是目标节点 then 输出并退出; if 新节点符合条件,并且新节点与原已产生的节点不重复 then tail指针加1,把新节点加入到队尾; end; until head=tail; end;
【广度优先搜索注意事项】 1、每生成一个子节点,就要提供指向它们父亲节点的指针。当解出现时候,通过逆向跟踪,可以找到从根节点到目标节点的一条路径。(当然不要求输出路径的,就没必要记住父亲节点); 2、生成的节点要与前面所有已经产生的节点比较,以免出现重复节点,浪费时间和空间,还有可能陷入死循环; 3、如果目标节点的深度与费用(如:路径长度)成正比,那么找到的第一个解即为最优解,这时,搜索速度比深度搜索要快些,在求最优解时往往采用广度优先搜索;如果节点的费用不与深度成正比时,第一次找到的解不一定是最优解。
【算法分析】看图很容易想到用邻接矩阵来存储顶点之间的关系,0表示有通路,即有边;1表示没有通路,即没有边存在。 定义一个a数组,充当存储扩展节点的队列,a[i].city记录经过的城市,a[i].pre记录前驱城市,这样就可以倒推出最短线路了,具体过程如下: 1、将城市A入队,队首指针为0,队尾指针为1; 2、将队首所指相连的城市依次入队(注意的是该城市在队列中未曾出现过),同时将入队城市的pre指向队首位置。然后将队首指针加1,得到新的队首城市。重复以上操作步骤,直到搜到H城市。利用pre可以倒推出最少城市线路。 A B C D E F G H A 1 0 0 0 1 0 1 1 B 0 1 1 1 1 0 1 1 B F C 0 1 1 0 0 1 1 1 A D G H D 0 1 0 1 1 1 0 1 C E E 1 1 0 1 1 1 0 0 F 0 0 1 1 1 1 1 0 G 1 1 1 0 0 1 1 0 H 1 1 1 1 0 0 0 1 例1 如图 是从 城市A到城市H 的交通图。从图中可以看出,从城市A到城市H要经过若干个城市。现在要找出一条经过城市最少的一条路线。 矩阵存储顶点关系
【参考程序】 Program ex_1; const ju:array[1..8,1..8] of 0..1=( (1,0,0,0,1,0,1,1), (0,1,1,1,1,0,1,1), (0,1,1,0,0,1,1,1), (0,1,0,1,1,1,0,1), (1,1,0,1,1,1,0,0), (0,0,1,1,1,1,1,0), (1,1,1,0,0,1,1,0), (1,1,1,1,0,0,0,1)); type node=record city:char; pre:integer; end; var head,tail,i:integer; a:array[1..100] of node; s:array[‘A’..’H’] of boolean; procedure print(d:integer); begin write(a[d].city); repeat d:=a[d].pre; write(‘--------’,a[d].city); until a[d].pre=0; end; Procedure doit; begin fillchar(s,sizeof(s),true); head:=0; tail:=1; a[1].city:=‘A’; a[1].pre:=0;s[a[1].city]:=false; repeat inc(head); for i:=1 to 8 do if (ju[ord(a[head].city)-64,i]=0)and (s[chr(i+64)]=true) then begin inc(tail); a[tail].city:=chr(i+64); a[tail].pre:=head; s[a[tail].city]:=false; if a[tail].city=‘H’ then begin print(tail); break; end; end; until head=tail; end; Begin doit; End.
【算法分析】 1、读入m*n矩阵阵列,将其转换成为boolean矩阵存入bz数组中; 2、沿bz数组矩阵从上到下、从左到右,找到遇到的第一个细胞; 3、将细胞的位置入队h,并沿其上、下、左、右四个方向上的细胞位置入队,入队后的位置bz数组置为false; 4、将h队头出队,沿其上、下、左、右四个方向上的细胞位置入队,入队后的位置bz数组置为false; 5、重复4,直到h队空为止,则此时找到了一个细胞; 6、重复2,直至矩阵找不到细胞; 7、输出找到的细胞个数。 例2 一矩形阵列由数字0-9组成,数字1-9代表细胞,细胞的定义是沿细胞数字上、下、左、右如果还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。 • 如阵列: • 10 • 0234500067 • 1034560500 • 2045600671 • 0000000089
【参考程序】 program cell; const dx:array[1..4] of -1..1=(-1,0,1,0); dy:array[1..4] of -1..1=(0,1,0,-1); var name,s:string; n,m,i,j,num:integer; pic:array[1..50,1..50] of integer; bz:array[1..50,1..50] of boolean; h:array[1..1000,1..2] of integer; procedure doit(p,q:integer); var i,t,w,x,y:integer; begin inc(num);bz[p,q]:=false; t:=1;w:=1;h[1,1]:=p;h[1,2]:=q; repeat for i:=1 to 4 do begin x:=h[t,1]+dx[i];y:=h[t,2]+dy[i]; if (x>0)and(x<=m)and(y>0)and(y<=n)and(bz[x,y]) then begin inc(w);h[w,1]:=x;h[w,2]:=y;bz[x,y]:=false; end; end; inc(t); until t>w; end; begin fillchar(bz,sizeof(bz),true); num:=0; readln(m,n); for i:=1 to m do begin readln(s); for j:=1 to n do begin pic[i,j]:=ord(s[j])-48; if pic[i,j]=0 then bz[i,j]:=false; end; end; for i:=1 to m do for j:=1 to n do if bz[i,j] then doit(i,j); writeln(num); end.
19 18 12 15 16 17 13 14 1 2 3 9 10 11 7 4 5 6 8 上机练习 1、最短路径 如图,从入口(1)到出口(17)的可行路线图中,数字标号表示关卡。请编程求从入口到出口经过最少关卡路径的算法。 提示:用邻接矩阵存储关卡之间的关系与前驱。 【输出样例】 171619181
入口 0 -1 0 0 0 0 0 0 -1 0 0 0 0 -1 0 0 0 -1 -1 0 0 0 0 0 -1 -1 -1 0 0 -1 -1 0 0 0 0 0 出口 0 0 0 0 0 0 0 -1 -1 2、迷宫问题 如图,给一个n*m的迷宫图和一个入口、一个出口。编程打印一条从迷宫入口到出口的路径。这里黑色方块的单元表示走不通(用-1表示),黄色单元表示可以走(用0表示),只能往上、下、左、右四个方向走,如果无路则输出“No way!”。(注:只输出一条就可以了) 【提示】本题深搜和广搜都可以,请同学们动动脑子,有条件的可以两种方法都试试看。
f:=false;k:=1;head:=0;tail:=1;route[1].x:=soux; route[1].y:=souy;route[1].pre:=0;map[soux,souy]:=-1; repeat inc(head); for i:=1 to 4 do begin x:=route[head].x+dx[i]; y:=route[head].y+dy[i]; if (x>0)and(x<=n)and(y>0)and(y<=m)and(map[x,y]=0) then begin inc(tail); route[tail].x:=x;route[tail].y:=y;route[tail].pre:=head; map[x,y]:=-1; if (x=desx)and(y=desy) then begin f:=true; print(tail); break; end; end; end; writeln; if f then begin writeln(k);break;end; until head=tail; if not f then writeln('No way!'); end. program exp2; const maxn=50; dx:array[1..4] of integer=(1,0,-1,0); dy:array[1..4] of integer=(0,1,0,-1); var map:array[1..maxn,1..maxn] of integer; f:boolean; n,m,i,j,desx,desy,soux,souy,head,tail,x,y,k:integer; route:array[1..maxn] of record x,y,pre:integer;end; procedure print(d:integer); begin if route[d].pre<>0 then begin print(route[d].pre);inc(k);end; write('(',route[d].x,',',route[d].y,') '); end; begin readln(n,m); for i:=1 to n do for j:=1 to m do read(map[i,j]); readln(soux,souy); readln(desx,desy);
3、硬币翻转 在桌面上有一排硬币,共n枚,每一枚硬币均为正面向上。现在要把所有的硬币翻成反面向上,规则是每次可翻转任意n-1枚硬币(正面向上的被翻转为反面向上,反之亦然)。求一个最短的操作序列(将每次翻转n-1枚硬币定为一次操作)。 【输入样例】 4 【输出样例】 4 {操作次数} 0111 {每次操作后硬币状态,0表示正面向上,1表示反面向上} 1100 0001 1111