广度搜索.docx

上传人:b****5 文档编号:5223454 上传时间:2022-12-14 格式:DOCX 页数:18 大小:55.13KB
下载 相关 举报
广度搜索.docx_第1页
第1页 / 共18页
广度搜索.docx_第2页
第2页 / 共18页
广度搜索.docx_第3页
第3页 / 共18页
广度搜索.docx_第4页
第4页 / 共18页
广度搜索.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

广度搜索.docx

《广度搜索.docx》由会员分享,可在线阅读,更多相关《广度搜索.docx(18页珍藏版)》请在冰豆网上搜索。

广度搜索.docx

广度搜索

第二节广度优先搜索

一、广度优先搜索

在深度优先搜索中,深度越大的结点越先得到扩展;如果将深度越小的结点越先得到扩展,那便是广度优先搜索。

广度优先搜索类似于树的按层次遍历的过程,它从初始点开始,应用算符生成第一层结点,检查目标结点是否在这些后继结点中;若没有,再用算符将所有第一层的结点逐一扩展,生成第二层结点,并逐一检查第二层结点中是否包含目标结点;若没有,再用算符将所有第二层的结点逐一扩展,生成第三层结点,并逐一检查第三层结点中是否包含目标结点;……如此依次扩展、检查下去,直到发现目标结点为止。

广度优先搜索为了满足先生成的结点先扩展的原则,采用队列的数据结构来存贮结点。

队列是一种线性表,对于它所有的进队都在表尾的一端进行,所有的出队都在表首的一端进行。

如同现实生活中的等车、买票的排队,新来的总是加入队尾,每次离开的总是队首的人。

例1-2-1下面是六个城市之间道路联系的示意图,连线表示两城市之间有道路相通。

请编一程序,由计算机找出从C1城到C6城的没有重复城市的所有不同的路径。

const

link:

array[1..5,1..6]ofbyte=((0,1,1,0,0,0),

(1,0,1,1,1,0),

(1,1,0,1,1,0),

(0,1,1,0,1,1),

(0,1,1,1,0,1));

type

ft=setof1..6;

var

qm{队列},pnt{parent父节点}:

array[1..100]ofbyte;

f{走到这里通过的城市}:

array[1..100]offt;

fs:

ft;

i,k,closed,open:

byte;

procedureprint;

var

n,i,j:

byte;

s:

array[1..6]ofbyte;

begin

i:

=open;

n:

=0;

whilei>0dobegin

n:

=n+1;

s[n]:

=i;

i:

=pnt[i];

end;

write('Dep=',n-1,':

1');

forj:

=n-1downto1dobegin

write('->',qm[s[j]]);

end;

readln;

end;

begin

f[1]:

=[1]{城市1已到};qm[1]:

=1{队列的第一个节点是1};pnt[1]:

=0;{没有父辈节点}

closed:

=0;open:

=1;

repeat

inc(closed);

k:

=qm[closed];

ifk<>6thenbegin

fs:

=f[closed];

fori:

=2to6do

if(not(iinfs))and(link[k,i]>0)thenbegin

inc(open);

qm[open]:

=i;

f[open]:

=fs+[i];

pnt[open]:

=closed;

ifi=6thenprint;

end;

end;

untilclosed>=open;

end.

在广度优先搜索中,我们将扩展出来的结点存贮在一个称作qm的数组里,qm数组采用“先进先出”的队列结构,设两个指针closed和open,分别是队首指针和队尾指针。

其中qm[1..closed-1]存贮已扩展的结点(即这些结点的子结点已扩展出);qm[closed..open]存贮待扩展结点(即这些结点的子结点尚待扩展)。

当closed>=open则表示队列空,结束。

pnt为父辈结点数组,它记录了每个结点的父辈结点,当找到目标后,可沿着父辈结点倒串上去,输出路径方案。

在广度优先搜索中,第一个达到目标结点的,即是最短路径。

例1-2-2有一个由四个1和四个0,中间有一个空格组成的字符串‘11110000’,现规定:

①1只能向右运动,0只能向左运动。

②空格左右的1或0可以移动,进入空格。

③1可以跳过一个0,进入空格;0也可以跳过一个1,进入空格。

要求在符合上述规定的方式下,以最少的步骤,将其变为字符串‘00001111’,编程打印输出运动的每一步。

type

a9=string[9];

qt=record

a:

a9;

x,pnt:

byte;

end;

var

qm:

array[byte]ofqt;

temp:

qt;

closed,open:

byte;

Procedureprint;

Var

buf:

array[1..30]ofbyte;

i,j:

byte;

begin

i:

=open;j:

=0

whilei>0dobegin

inc(j);

buf[j]:

=i;

i:

=qm[i].pnt;

end;

fori:

=jdownto1dobegin

write('No.',i-j:

2,':

',qm[buf[i]].a);

readln;

end;

halt;

end;

procedurecomp;

vari:

byte;

begin

fori:

=1toopendo

ifqm[i].a=temp.athenexit;

open:

=open+1;

qm[open].a:

=temp.a;

qm[open].x:

=temp.x;

qm[open].pnt:

=closed;

iftemp.a='00001111'thenprint;

end;

procedureopa;

begin

temp:

=qm[closed];

withtempdo

if(x>1)and(a[x-1]='1')thenbegin

a[x-1]:

='';

a[x]:

='1';

x:

=x-1;

comp;

end;

end;

procedureopb;

begin

temp:

=qm[closed];

withtempdo

if(x<9)and(a[x+1]='0')thenbegin

a[x+1]:

='';

a[x]:

='0';

x:

=x+1;

comp;

end;

end;

procedureopc;

begin

temp:

=qm[closed];

withtempdo

if(x>2)and(a[x-1]='0')and(a[x-2]='1')thenbegin

a[x-2]:

='';

a[x]:

='1';

x:

=x-2;

comp;

end;

end;

procedureopd;

begin

temp:

=qm[closed];

withtempdo

if(x<8)and(a[x+1]='1')and(a[x+2]='0')thenbegin

a[x+2]:

='';

a[x]:

='0';

x:

=x+2;

comp;

end;

end;

begin

withqm[1]dobegin

a:

='11110000';

x:

=5;

pnt:

=0;

end;

closed:

=0;open:

=1;

whileclosed

closed:

=closed+1;

opa;

opb;

opc;

opd;

end;

end.

例1-2-3在魔方风靡全球后,Rubik先生发明了它的简化版——魔板,如下图:

魔板由6个同样大小的方块组成,每个方块的颜色均不相同,本题中用数字1–6分别表示,可能出现在魔板的任一位置。

对魔板可施加三种不同的操作,分别以a、b、c标识,具体操作方法如下:

应用三种基本操作,可由任一状态到达任意另一状态。

编一程序,对于输入的一个初始状态和一个目标状态,寻找一种最少的操作步骤,打印输出从初始状态到目标状态的每一步操作和状态值。

type

qt=record

x,y:

integer;

op:

char;

pnt:

integer;

end;

var

qm:

array[1..720]ofqt;

closed,open,k,l,m,n:

integer;

procedureprint;

var

s:

array[1..30]ofinteger;

i,j:

integer;

begin

j:

=0;

i:

=open;

whilei>0dobegin

j:

=j+1;

s[j]:

=i;

i:

=qm[i].pnt;

end;

writeln(qm[1].x);

writeln(qm[1].y);

writeln;

fori:

=j-1downto1dobegin

writeln('Oprate:

',qm[s[i]].op);

writeln(qm[s[i]].x);

writeln(qm[s[i]].y);

readln;

end;

halt;

end;

procedurecomp(opx:

char);

var

i:

integer;

begin

fori:

=1toopendo

if(m=qm[i].x)and(n=qm[i].y)thenexit;

open:

=open+1;

qm[open].x:

=m;qm[open].y:

=n;

qm[open].op:

=opx;qm[open].pnt:

=closed;

if(m=k)and(n=l)thenprint;

end;

procedureopa;

begin

m:

=qm[closed].y;

n:

=qm[closed].x;

comp('a');

end;

procedureopb;

begin

m:

=(qm[closed].xmod10)*100+(qm[closed].xdiv10);

n:

=(qm[closed].ymod10)*100+(qm[closed].ydiv10);

comp('b');

end;

procedureopc;

var

a,b,c,d:

integer;

begin

a:

=qm[closed].xdiv100;

b:

=qm[closed].xmod100div10;

c:

=qm[closed].ydiv100;

d:

=qm[closed].ymod100div10;

m:

=c*100+a*10+(qm[closed].xmod10);

n:

=d*100+b*10+(qm[closed].ymod10);

comp('c');

end;

begin

write('InputSourceM(1,2,3):

');readln(qm[1].x);

write('InputSourceN(6,5,4):

');readln(qm[1].y);

writeln;

write('InputObjectK(1,2,3):

');readln(k);

write('InputObjectL(6,5,4):

');readln(l);

writeln;

qm[1].pnt:

=0;closed:

=0;open:

=1;

whileclosed

closed:

=closed+1;

opa;

opb;

opc;

end;

end.

例1-2-4八数码问题:

在3*3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一个数字,棋盘上留有一个空格,空格周围的棋子可以移到空格中。

要求给出一种初始状态和一种目标状态,找出一种最少步骤的移动方法,实现从初始状态到目标状态的转变。

例如:

初始状态目标状态

283123

16484

75765

Type

a33=array[1..3,1..3]ofbyte;

a4=array[1..4]of-1..1;

node=record

ch:

a33;

y,x:

byte;

pnt:

word;

end;

Const

start:

a33=((2,8,3),(1,6,4),(7,0,5));

goal:

a33=((1,2,3),(8,0,4),(7,6,5));

x1=2;y1=3;

max=4800;

dx:

a4=(0,-1,0,1);

dy:

a4=(-1,0,1,0);

Var

data:

array[1..max]ofnode;

temp:

node;

r,tx,ty:

byte;

closed,open:

word;

Functioncheck(k:

byte):

boolean;

begin

ty:

=temp.y+dy[k];

tx:

=temp.x+dx[k];

if(tyin[1..3])and(txin[1..3])

thencheck:

=true

elsecheck:

=false;

end;

Functiondupe:

boolean;

Var

i:

word;

j,k:

integer;

b:

boolean;

begin

i:

=0;

repeat

inc(i);

b:

=true;

forj:

=1to3do

fork:

=1to3do

ifdata[i].ch[j,k]<>data[open].ch[j,k]thenb:

=false;

untilbor(i>=open-1);

dupe:

=b;

end;

Functiongoals:

boolean;

Vari,j:

byte;

begin

goals:

=false;

fori:

=1to3do

forj:

=1to3do

ifdata[open].ch[i,j]<>goal[i,j]thenexit;

goals:

=true;

end;

Proceduretrace;

Vari,j:

byte;

begin

writeln('closed=',closed,'open=',open,'oprater=',r);

fori:

=1to3dobegin

forj:

=1to3do

ifdata[open].ch[i,j]=0thenwrite('')

elsewrite(data[open].ch[i,j]);

writeln

end;

readln;

end;

Procedureprint;

Var

b:

array[1..20]ofword;

i,j:

word;

k,n:

byte;

begin

n:

=0;

i:

=open;

whilei>0dobegin

inc(n);

b[n]:

=i;

i:

=data[i].pnt;

end;

writeln('Staps:

',n-1:

5);

fork:

=ndownto1dobegin

fori:

=1to3dobegin

forj:

=1to3do

ifdata[b[k]].ch[i,j]=0

thenwrite('')

elsewrite(data[b[k]].ch[i,j]);

writeln;

end;

readln;

end;

halt;

end;

begin

closed:

=0;open:

=1;

withdata[1]dobegin

ch:

=start;

y:

=y1;x:

=x1;

pnt:

=0;

end;

repeat

inc(closed);

temp:

=data[closed];

forr:

=1to4do

ifcheck(r)thenbegin

inc(open);

data[open]:

=temp;

withdata[open]dobegin

ch[y,x]:

=ch[ty,tx];

ch[ty,tx]:

=0;

y:

=ty;x:

=tx;

pnt:

=closed;

end;

{trace;}

ifdupethendec(open)

elseifgoalsthenprint;

end;

until(closed>=open)or(open>max-3);

writeln('Nosolution!

');

readln;

end.

 

二、队列与广度优先搜索

队列是不同于栈的另一种线性表。

在日常生活中,无论是购物、订票或候车都有可能要排队。

排队所遵循的原则是“先来先服务”,后来者总是加到队尾,排头者总是先离开队伍。

队列就是从日常生活中的排队现象抽象出来的。

所谓队列,就是允许在一端进行插入,在另一端进行删除的线性表。

允许插入的一端称为队尾,通常用一个队尾指针open指向队尾元素,即open总是指向最后被插入的元素;允许删除的一端称为队首,通常也用一个队首指针closed指向排头元素的前面。

初始时closed=open=0(如图)。

显然,在队列这种数据结构中,最先插入在元素将是最先被删除;反之最后插入的元素将最后被删除,因此队列又称为“先进先出”(FIFO—firstinfirstout)的线性表。

与栈相似,队列的顺序存储空间可以用一维数组q[1‥m]模拟:

Q:

1m

我们按照如下方式定义队列:

Const

M=队列元素的上限;

Type

Equeue=array[1..m]ofqtype;{队列的类型定义}

Var

qm:

equeue;{队列}

open,closed:

integer;{队尾指针和队首指针}

队列的运算主要有两种

1.过程ADD(qm,x,open)—在队列qm的尾端插入元素x

procedureADD(varqm:

equeue;x:

qtype;varopen:

integer);

begin

ifopen=m

thenwriteln(‘Overflow’){上溢}

elsebegin{后移队尾指针并插入元素x}

open:

=open+1;qm[open]:

=x;

end;

end;

2.过程DEL(qm,y,closed,open)—取出qm队列的队首元素y

procedureDEL(varqm:

equeue;vary:

qtype;varclosed,open:

integer);

begin

ifclosed=open

thenwriteln(‘underflow’){下溢}

elsebegin{后移队首指针并取出队首元素}

closed:

=closed+1;y:

=qm[closed];

end;

end;

由于队列只能在一端插入,在另一端删除,因此随着入队及出队运算的不断进行,就会出现一种有别于栈的情形:

队列在数组中不断地向队尾方向移动,而在队首的前面产生一片不能利用的空闲存储区,最后会导致当尾指针指向数组最后一个位置(即open=m)而不能再加入元素时,存储空间的前部却有一片存储区无端浪费,这种现象称为“假溢出”。

下图给出了一个“假溢出”的示例:

m

m

m

Open→m

Am

“假溢出”

A4

open→3

A3

closed→3

Closed→3

2

A2

2

2

1

1

A1

1

1

Closed,open→Closed→

初始时队列空加入三个元素删除三个元素队列空加入m-3个元素队列满

closed=open=0closed=0open=3closed=open=3closed=3open=m

为了解决“假溢出”的问题,我们不妨作这样的设想:

在队列中,当存储空间的最后一个位置已被使用而要进行入队运算时,只要存储空间第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。

采用首尾相接的队列结构后,可以有效地解决假溢出的问题,避免数据元素的移动,这就是所谓的循环队列。

下图给出了循环队列的结构。

循环队列将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用,循环队列的存取方法亦为“先进先出”。

对循环队列操作有以下几种状态:

初始时队列空,队首指针和队尾指针均指向存储空间的最后一个位置,

即closed=open=m。

●入队运算时,尾指针进一,即

open:

=open+1;

ifopen=m+1thenopen:

=1;

这两条语句可用一条语句替代:

open:

=openmodm+1;

●出队运算时,首指针进一,即

closed:

=closed+1;ifclosed=m+1thenclosed:

=1;

这两条语句可用一条语句替代:

closed:

=closedmodm+1;

●队列空时有closed=open。

●队列满时有closed=openmodm+1。

(为了区分队列空和队列满,改用“队尾指针追上队首指针”这一特征作为队列满标志。

这种处理方法的缺点是浪费队列空间的一个存储单元)

循环队列的运算有两种:

1.过程ADD2(qm,x,open)—在循环队列qm中插入一个新元素x

procedureADD2(varqm:

e

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 艺术

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1