深度优先搜索算法

发布时间:2012-04-12 21:01:33   来源:文档文库   
字号:

深度优先搜索算法教程

[1] ABCDE五本书,要分给张、王、刘、赵、钱五位同学,每人只能选一本。事先让每个人将自己喜爱的书填写在下表中。希望你设计一个程序,打印分书的所有可能方案,当然是让每个人都满意。(如下图所示)


[分析] 这个问题中喜爱的书是随机的,没有什么规律,所以用穷举法比较合适。

为编程方便,用12345分别表示这五本书。这五本书的一种全排列就是五本书的一种分法。例如54321表示第5本书(即E)分给张,第4本书(即D分给王,)……1本书(即A分给钱)。“喜爱书表”可以用二维数组来表示,1表示喜爱,0表示不喜爱。

[算法设计]

1、 产生5个数字的一个全排列;

2、 检查是否符合“喜爱书表”的条件,如果符合就打印出来。

3、 检查是否所有排列都产生了,如果没有产生完,则返回1

4、 结束。

[算法改进]:因为张只喜欢第34本书,这就是说,1* * * *一类的分法都不符合条件。所以改进后的算法应当是:在产生排列时,每增加一个数,就检查该数是否符合条件,不符合,就立即换一个,符合条件后,再产生下一个数。因为从第i本书到第i+1本书的寻找过程是相同的,所以可以用递归算法。算法如下:

procedure try(i); {给第I个同学发书}

begin

for j:=1 to 5 do

begin

if i个同学分给第j本书符合条件 then

begin

记录第i个数; {j}

if i=5 then 打印一个解 else try(i+1);

删去第i个数字

end

end

end;

具体如下:

◆递归算法

program zhaoshu;

const

like:array[1..5,1..5] of 0..1

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

name:array[1..5] of string[5] =('zhang','wang','liu','zhao','qian');

var

book:array[1..5] of 0..5;

flag:set of 1..5;

c:integer;

procedure print;

var i:integer;

begin

inc(c);

writeln('answer',c,':');

for i:=1 to 5 do

writeln(name[i]:10,':',chr(64+book[i]));

end;

procedure try(i:integer);

var j:integer;

begin

for j:=1 to 5 do

if not(j in flag) and (like[i,j]>0) then

begin

flag:=flag+[j]; book[i]:=j;

if i=5 then print else try(i+1);

flag:=flag-[j]; book[i]:=0;

end;

end;

{=====main====}

begin

flag:=[]; c:=0;

try(1);

readln;

end.

C语言代码:

#include

#include

int like[5][5]={0,0,1,1,0,1,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0,1,0,0,1};

char name[5][10]={"zhang","wang","liu","zhao","qian"};

int flag[5]={1,1,1,1,1};

int book[5],c=0;

void print()

{ int i;

c++;

printf("answer %d:",c);

for(i=0;i<=4;i++)

printf("%s:%c ",name[i],65+book[i]);

printf("\n");

}

void dsf(int i)

{

int j;

for(j=0;j<=4;j++)

if(flag[j]&&like[i][j])

{ flag[j]=0;book[i]=j;

if(i==4) print();

else dsf(i+1);

flag[j]=1;book[i]=0;

}

}

int main()

{ dsf(0);

system("pause");

return 0;

}

 

◆非递归算法

program path;

dep:=0; {dep为栈指针,也代表层次}

repeat

dep:=dep+1; r:=0; p:=false;

repeat r:=r+1;

if 子节点mr符合条件 then

产生新节点并存于dep指向的栈顶;

if 子节点是目标 then 输出并退出(或退栈)

else p:=true;

else if r>=maxr then 回溯 else p:=flase;

endif;

until p:=true;

until dep=0;

其中回溯过程如下:

procedure 回溯;

dep:=dep-1;

if dep=0 then p:=true else 取回栈顶元素(出栈);

具体如下:

◆非递归算法

program zhaoshu2;

uses crt;

const

like:array[1..5,1..5] of 0..1

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

name:array[1..5] of string[5]

=('zhang','wang','liu','zhao','qian');

var book:array[0..5] of 0..5;

flag:set of 1..5;

c,dep,r:longint;

p:boolean;f:text;

procedure print;

var i:integer;

begin

inc(c);

writeln(f,'answer',c,':');

for i:=1 to 5 do

writeln(f,name[i]:10,':',chr(64+book[i]));

end;

procedure back;

begin

dep:=dep-1;

if dep=0 then p:=true

else begin r:=book[dep]; flag:=flag-[r]; end;

end;

{================================main========================}

begin

assign(f,'d:\wuren.pas');

rewrite(f);

clrscr;

flag:=[]; c:=0;

dep:=0;

repeat

dep:=dep+1; r:=0; p:=false;

repeat

r:=r+1;

if not(r in flag) and (like[dep,r]>0)and (r<=5)then

begin

flag:=flag+[r]; book[dep]:=r;

if dep=5 then begin print;inc(dep);back; end

else p:=true;

end

else

if r>=5 then back else p:=false

until p=true;

until dep=0;

close(f);

end.

上述程序运行产生结点的过程如下图所示:

 

结点旁的编号是结点产生的先后顺序。从产生的顺序可以看出,深度越大的结点越先进行扩展,即先产生它的子结点。例如,有了结点(3)和(31)后,并不是继续产生(3)的子结点,而是先产生(31)的子结点(312)。这是由于(31)的深度是2,(3)的深度是1,(31)的深度大,所以先扩展。{同前边图的深度优先遍历联系起来}

这种在搜索过程中,深度大的节点先进行扩展的算法,称之为深度优先搜索法。简称DFS法。(Depth_First_Search)。

深度优先搜索的特点:

(1) 对已产生的节点按深度排序,深度大的先得到扩展,即先产生它的子节点。

(2) 深度大的节点是后产生的,但先得到扩展,即“后产生,先扩展”,因此,采用堆栈作为该算法的数据结构。

深度优先搜索的基本思想:

1、把初始状态赋到state(1)中,即初状态入栈,并初始化指针1––>object, 1––>top

(注释:state(x)表示节点x的状态,state(1)表示初始状态,object表示作扩展的节点。Top表示由节点object扩展的子节点。)

2、分别用waymax种途径扩展object的子节点。

(1)从途径1开始扩展新节点 1––>waynum

(注释:waymax表示所有可能扩展子节点的途径的总数目,waynum表示所选途径编号)

2)判断waynum途径可行吗?(即可否扩展新节点)若不行,转2––5)。

3)对新节点设父指针,新状态入栈。top+1––>topobject––>father(top)state(object)经途径waynum改变后的新状态––>state(top)中。

(注释:father(x)节点x 的父节点的标号。)

4top是目标节点吗?是,则调用打印结果子程序print打印结果路径。若只要求一个方案则结束,否则,让节点top出栈再继续执行下一步。

5)选择下一条路径,即waynum+1––>waynum,若waynum<=waymax,则转2––(2)

3、选择用来扩展的新节点。

1)若object,(即object有子节点)则转3––(3)

2top出栈,top-1––>top,若top=0(栈空则结束),若top已扩展,再转3––(2)

3top––>object,若object已规定深度,则转3––(2)。否则转2––(1)

深度优先搜索Pascal语言程序:

program dfs(input,output);

const n=扩展节点估计数;waymax=扩展节点途径数;

var state, fatherarray[1..n] of integer;

top, object, waynuminteger;

procedure print(vinteger);

begin

while v>0 do

begin write(state[v], '<==');

v=father[v]

end;

end;

begin (第一步)

state[1]=初状态;

object =1top:=1; father[1]:=0;

while top>0 do

begin

for waynum:=1 to waymax do 第二步

begin

if way(maxnum) 可行 then

begin

top:=top+1; father[top]:=object;

state[top]:=新状态;

if top 是目标节点 then

begin

print(top); top:=top-1;

end;

end;

end;

if top>object then object:=top 第三步

else repeat top:=top-1; object:=top until top<>father[top+1];

end;

end.

深度优先搜索的基本算法如下:

一、递归算法

递归过程为:

procedure DFS_TRY(i);

for r:=1 to maxr do

if 子结点mr符号条件 then

产生的子结点mr压入栈;

if 子结点mr是目标结点 THEN 输出

ELSE DFS_TRYI+1);

栈顶元素出栈(删去mr;

ENDIF

ENDDO

主程序为:

PROGRAM DFS

初始状态入栈;

DFS_TRY1);

二、非递归算法:

PROGRAM DFSDEP);

DEP=0

REPEAT

DEP=DEP+1

J=0P=FALSE

REPEAT

J=J+1

IF MR 符合条件 THEN

产生子结点MR并将其记录;

IF 子结点是目标 THEN 输出并出栈

ELSE P=TRUE

ELSE

IF J>=MAXJ THEN回溯 ELSE P=FALSE

ENDIF

UNTIL P=TRUE

UNTIL DEP=0

其中回溯过程如下:

PROCEDURE BACKTRACKING

DEP=DEP-1

IF DEP>0 THEN 取回栈顶元素

ELSE P=TRUE

不同问题的深度优先搜索基本算法是一样的,但在具体处理方法上,编程的技巧上,不尽相同,甚至会有很大差别。

比如例1的解法还可以这样来设计:从表中看出,赵同学只喜爱D一本书,无其他选择余地。因此可以在搜索前就确定下来。为了编程方便,把赵钱二人位置互换,这样程序只需对张王刘钱四人进行搜索。

另外,发现表示“喜爱书表”的数组有多个0,为减少不必要的试探,我们改用链表来表示。例如第3位同学的链表是:LIKE30=2,表示他喜爱的第一本书编号是3,最后LIKE33=0,表示3是最后一本喜爱的书。

深度优先搜索算法小结

1可以用深度优先搜索法处理的问题是各种各样的。有的搜索深度是已知和固定的,有的是未知的,有的搜索深度是有限制的,但达到目标的深度不定,但我们也看到,无论问题的内容、性质、求解要求如何不同,但它们的程序结构都是相同的,即都是第四节中描述的算法结构,不相同的仅仅是存储结点的数据库结构、产生规则和输出要求。

2深度优先搜索法有递归和非递归两种设汁方法.一般地,当搜索深度较小、问题递归形式较明显时,用递归方法设计较好,它可以使程序结构更简捷易懂。但当搜索深度较大,由于系统堆栈容量的限制,递归易产生溢出,用非递归设计方法较合适。

3探度优先搜索法有广义和狭义两种理解。广义的理解是只要最新产生的结点(即深度最大的结点)先进行扩展的搜索法,就称为深度优先搜索。在这种理解情况下,深度优先搜索算法有全部保留和不全部保留产生的结点两种情况,而狭义的理解是,仅仅指保留全部产生的结点的算法。本书取前一种广义的理解。不保留全部结点的算法,属于一般的回溯算法范畴。保留全部结点的算法,实际上是在数据库中产生结点之间的搜索树,因此也属于图搜索算法的范畴。

4不全部保留结点的深度优先搜索法,由于把扩展完的结点从数据库中弹出删去,这样,一般在数据库中存储的结点数就是深度值,因此它占用的空间较少。所以,当搜索树的结点较多,用其他方法易产生内存溢出时,深度优先搜索不失为一种有效的求解方法。

5从城市路径的输出结果可看出,深度优先搜索法找到的第一个解,不一定是最优解。例如城市路径的最优解应是COST=13,但第一个解却是COST17

如果要求出最优解,一种办法是用第九章将要介绍的动态规划法,另一种办法是修改原算法:把原输出过程的地方改为记录过程,即记录达到当前目标的路径和相应的路程值,并与前面巳记录的值加以比较,保留其中最优解。等全部搜索完成后,再把保留的最优解输出。

练习题:
1、八皇后问题:现要将八个皇后放置在8×8的棋盘上,要求任意两个皇后不能在同一行、同一列、同一条对角线上,打印出皇后的放置位置。

2、求出从1..NN个数中取M个数的所有组合(M<=N

3、求出从1..NN(0个数中取M个数的所有排列(M<=N)

4、仅含有大写字母的的字符串“BFY”称为是良序的,因为它的字母是按ASCII码严格地从小到大排列的,而“BAY”就不是。请编一程序,从键盘输入自然数n(2<=n<=26)后,打印出所有长度为n良序字符串。

5、地图的着色。一张地图有12个区域,用四种颜色着色,要求相邻的区域用不同的颜色。问有几种方案,打印出这些方案来。

本文来源:https://www.2haoxitong.net/k/doc/b36ca3bac77da26925c5b0b4.html

《深度优先搜索算法.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式