_广度优先搜索

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第八章广度优先搜索广度优先搜索的过程广度优先搜索算法(又称宽度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。广度优先算法的核心思想是:从初始节点开始,应用算符生成第一层节点,检查目标节点是否在这些后继节点中,若没有,再用产生式规则将所有第一层的节点逐一扩展,得到第二层节点,并逐一检查第二层节点中是否包含目标节点。若没有,再用算符逐一扩展第二层的所有节点……,如此依次扩展,检查下去,直到发现目标节点为止。即⒈从图中的某一顶点V0开始,先访问V0;⒉访问所有与V0相邻接的顶点V1,V2,......,Vt;⒊依次访问与V1,V2,......,Vt相邻接的所有未曾访问过的顶点;⒋循此以往,直至所有的顶点都被访问过为止。这种搜索的次序体现沿层次向横向扩长的趋势,所以称之为广度优先搜索。广度优先搜索算法描述:ProgramBfs;初始化,初始状态存入队列;队列首指针head:=0;尾指针tail:=1;repeat指针head后移一位,指向待扩展结点;forI=1tomaxdo//max为产生子结点的规则数beginif子结点符合条件thenbegintail指针增1,把新结点存入列尾;if新结点与原已产生结点重复then删去该结点(取消入队,tail减1)elseif新结点是目标结点then输出并退出;end;end;until(head=tail);//队列为空广度优先搜索注意事项:1、每生成一个子结点,就要提供指向它们父亲结点的指针。当解出现时候,通过逆向跟踪,找到从根结点到目标结点的一条路径。当然不要求输出路径,就没必要记父亲。2、生成的结点要与前面所有已经产生结点比较,以免出现重复结点,浪费时间,还有可能陷入死循环。3、如果目标结点的深度与“费用”(如:路径长度)成正比,那么,找到的第一个解即为最优解,这时,搜索速度比深度搜索要快些,在求最优解时往往采用广度优先搜索;如果结点的“费用”不与深度成正比时,第一次找到的解不一定是最优解。4、广度优先搜索的效率还有赖于目标结点所在位置情况,如果目标结点深度处于较深层时,需搜索的结点数基本上以指数增长。下面我们看看怎样用宽度优先搜索来解决八数码问题。例如图2给出广度优先搜索应用于八数码难题时所生成的搜索树。搜索树上的所有结点都标记它们所对应的状态,每个结点旁边的数字表示结点扩展的顺序。粗线条路径表明求得的一个解。从图中可以看出,扩展第26个结点,总共生成46个结点之后,才求得这个解。此外,直接观察此图表明,不存在有更短走步序列的解。【例1】图4表示的是从城市A到城市H的交通图。从图中可以看出,从城市A到城市H要经过若干个城市。现要找出一条经过城市最少的一条路线。【算法分析】看到这图很容易想到用邻接距阵来表示,0表示能走,1表示不能走。如图。首先想到的是用队列的思想。a数组是存储扩展结点的队列,a[i].city记录经过的城市,a[i].pre记录前趋城市,这样就可以倒推出最短线路。具体过程如下:(1)将城市A入队,队首为0、队尾为1。(2)将队首所指的城市所有可直通的城市入队(如果这个城市在队列中出现过就不入队,可用一个集合来判断),将入队城市的pre指向队首的位置。然后将队首加1,得到新的队首城市。重复以上步骤,直到搜到城市H时,搜索结束。利用pre可倒推出最少城市线路。【参考程序】ProgramEx8_1;constju:array[1..8,1..8]of0..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));typenode=record//记录定义city:char;pre:integer;end;varhead,tail,i:integer;a:array[1..100]ofnode;s:setof'A'..'H';procedureout(d:integer);//输出过程beginwrite(a[d].city);repeatd:=a[d].pre;write('--',a[d].city);untila[d].pre=0;writeln;end;proceduredoit;beginhead:=0;tail:=1;a[1].city:=‘A’;a[1].pre:=0;s:=[‘A’];repeat//步骤2inc(head);//队首加一,出队fori:=1to8do//搜索可直通的城市if(ju[ord(a[head].city)-64,i]=0)and(not(chr(i+64)ins))then//判断城市是否走过begininc(tail);//队尾加一,入队a[tail].city:=chr(64+i);a[tail].pre:=head;s:=s+[a[tail].city];ifa[tail].city='H'thenbeginout[tail];break;end;end;untilhead=tail;end;BEGIN//主程序doit;END.输出:H--F--A【例2】一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。如:阵列4100234500067103456050020456006710000000089有4个细胞。【算法分析】⑴从文件中读入m*n矩阵阵列,将其转换为boolean矩阵存入bz数组中;⑵沿bz数组矩阵从上到下,从左到右,找到遇到的第一个细胞;⑶将细胞的位置入队h,并沿其上、下、左、右四个方向上的细胞位置入队,入队后的位置bz数组置为FLASE;⑷将h队的队头出队,沿其上、下、左、右四个方向上的细胞位置入队,入队后的位置bz数组置为FLASE;⑸重复4,直至h队空为止,则此时找出了一个细胞;⑹重复2,直至矩阵找不到细胞;⑺输出找到的细胞数。programEx8_2;constdx:array[1..4]of-1..1=(-1,0,1,0);dy:array[1..4]of-1..1=(0,1,0,-1);varname,s:string;pic:array[1..50,1..79]ofinteger;bz:array[1..50,1..79]ofboolean;m,n,i,j,num:integer;h:array[1..4000,1..2]ofinteger;proceduredoit(p,q:integer);vari,t,w,x,y:integer;begininc(num);bz[p,q]:=false;t:=1;w:=1;h[1,1]:=p;h[1,2]:=q;//遇到的第一个细胞入队repeatfori:=1to4do//沿细胞的上下左右四个方向搜索细胞beginx:=h[t,1]+dx[i];y:=h[t,2]+dy[i];if(x0)and(x=m)and(y0)and(y=n)andbz[x,y]thenbegininc(w);h[w,1]:=x;h[w,2]:=y;bz[x,y]:=false;end;end;//本方向搜索到细胞就入队inc(t);//队头指针加1untiltw;//直至队空为止end;beginfillchar(bz,sizeof(bz),true);num:=0;readln(m,n);fori:=1tomdobeginreadln(s);forj:=1tondobeginpic[i,j]:=ord(s[j])-ord(‘0’);ifpic[i,j]=0thenbz[i,j]:=false;end;end;fori:=1tomdoforj:=1tondoifbz[i,j]thendoit(i,j);//在矩阵中寻找细胞writeln('NUMBERofcells=',num);readln;end.【例3】最短路径(1995年高中组第4题)如下图所示,从入口(1)到出口(17)的可行路线图中,数字标号表示关卡。现将上面的路线图,按记录结构存储如下图6:请设计一种能从存储数据中求出从入口到出口经过最少关卡路径的算法。【算法分析】该题是一个路径搜索问题,根据图示,从入口(1)到出口(17)可能有多条途径,其中最短的路径只有一条,那么如何找最短路径呢?根据题意,用数组NO存储各关卡号,用数组PRE存储访问到某关卡号的前趋关卡号。其实本题是一个典型的图的遍历问题,我们可以采用图的广度优先遍历,并利用队列的方式存储顶点之间的联系。从入口(1)开始先把它入队,然后把(1)的所有关联顶点都入队,即访问一个顶点,将其后继顶点入队,并存储它的前趋顶点,……,直到访问到出口(17)。最后,再从出口的关卡号(17)开始回访它的前趋关卡号,……,直到入口的关卡号(1),则回访的搜索路径便是最短路径。从列表中可以看出出口关卡号(17)的被访问路径最短的是:(17)←(16)←(19)←(18)←(1)由此,我们得到广度优先遍历求最短路径的基本方法如下:假设用邻接矩阵存放路线图(a[I,j]=1表示I与j连通,a[I,j]=0表示I与j不连通)。再设一个队列和一个表示拓展到哪个顶点的指针变量pos。(1)从入口开始,先把(1)入队,并且根据邻接矩阵,把(1)的后继顶点全部入队,并存储这些后继顶点的前趋顶点为(1);再把pos后移一个,继续拓展它,将其后继顶点入队,并存储它们的前趋顶点,……,直到拓展到出口(目的地(17));注意后继顶点入队前,必须要检查这个顶点是否已在队列中,如果已经在了就不要入队了;这一步可称为图的遍历或拓展;(2)从队列的最后一个关卡号(出口(17))开始,依次回访它的前驱顶点,倒推所得到的路径即为最短路径。主要是依据每个顶点的前趋顶点倒推得到的。实现如下:I:=1;WHILENO[I]17DOI:=I+1;REPEATWRITE(‘(‘,NO[I],’)’);WRITE(‘←’);I:=PRE[I];UNTILI=0;【参考程序】留给同学们完成,文件名ex8_3.pas。【例4】迷宫问题如下图所示,给出一个N*M的迷宫图和一个入口、一个出口。编一个程序,打印一条从迷宫入口到出口的路径。这里黑色方块的单元表示走不通(用-1表示),白色方块的单元表示可以走(用0表示)。只能往上、下、左、右四个方向走。如果无路则输出“noway.”。入口→0-1000000-10000-1000-1-100000-1-1-100-1-100000→出口0000000-1-1【算法分析】只要输出一条路径即可,所以是一个经典的回溯算法问题,本例给出了回溯(深搜)程序和广搜程序。实现见参考程序。【深搜参考程序】programEX8_4_1;constmaxn=50;varmap:array[1..maxn,1..maxn]ofinteger;f:boolean;n,m,i,j,desx,desy,soux,souy,totstep:integer;route:array[1..maxn]ofrecordx,y:integer;end;proceduremove(x,y,step:integer);beginmap[x,y]:=step;//走一步,作标记,把步数记下来route[step].x:=x;route[step].y:=y;//记路径if(x=desx)and(y=desy)thenbeginf:=true;totstep:=step;endelsebeginif(ym)and(map[x,y+1]=0)thenmove(x,y+1,step+1);//向右ifnotfand(xn)and(map[x+1,y]=0)th

1 / 26
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功