实验5基本检索与周游方法算法设计.docx
《实验5基本检索与周游方法算法设计.docx》由会员分享,可在线阅读,更多相关《实验5基本检索与周游方法算法设计.docx(146页珍藏版)》请在冰点文库上搜索。
实验5基本检索与周游方法算法设计
《算法分析与设计》实验报告
实验5基本检索与周游方法算法设计
姓名xxx学号xxxxx班级xxxxxxx
时间:
xxxxxx地点:
xxxx
同组人:
无
指导教师:
xxxxx
实验目的
1.掌握基本检索与周游方法算法设计的一般思想。
2.掌握二元树、图的周游和检索算法。
3.理解树、与或树、对策树周游与检索的思想和方法。
实验内容
1.二元树周游
2.图的周游
3.准备模拟二元树和模拟图的数据。
4.用递归方法设计二元树周游和检索程序,调试通过。
周游和检索的次序可
以是先序、中序和后序。
5.用非递归方法设计二元树周游和检索程序,调试通过。
周游和检索的次序可以是先序、中序和后序。
6.用递归方法设计图的周游程序,调试通过。
周游的次序可以是深度优先,也可以是宽度优先。
7.用非递归方法设计图的周游程序,调试通过。
周游的次序可以是深度优先,也可以是宽度优先。
1
实验环境
硬件:
Intel(R)Pentium(R)CPURAM:
4G
软件:
Myeclipse2013
编程语言:
Java
实验前准备
1、算法设计:
二元树周游
a)、中根次序周游(LDR)
ProcedureINORDER(T)
//T是一棵二元树。
T的每个结点有三个信息段:
LCHILD、DATA、RCHILD//
IfT≠0thencallINORDER(LCHILD(T))
callVISIT(T)
callINORDER(RCHILD(T))
endif
endINORDER
b)、先根次序周游(DLR)
ProcedurePREORDER(T)
//T是一棵二元树。
T的每个结点有三个信息段:
LCHILD、DATA、RCHILD//
IfT≠0thencallVISIT(T)
callPREORDER(LCHILD(T))
callPREORDER(RCHILD(T))
endif
endPREORDER
c)、后根次序周游(LRD)
ProcedurePOSTORDER(T)
//T是一棵二元树。
T的每个结点有三个信息段:
LCHILD、DATA、RCHILD//
IfT≠0thencallPOSTORDER(LCHILD(T))
callPOSTORDER(RCHILD(T))
callVISIT(T)
endif
endINORDER
d)、中根次序周游的非递归算法
ProcedureINORDER1(T)
//T是一棵二元树,每个结点有三个信息段:
LCHILD、DATA、
RCHILD//
//使用大小为m的栈的一个非递归算法//
1integeri,m,STACK(M)
2
2
ifT=0
then
return
endif
//T为空//
3
P←T;i←0//
周游T;i
为栈顶//
4
Loop
5
While
LCHILD(P)
≠0
do
//周游左子树//
6
i←i+1
7
If
i>mthenprint(
‘stackoverflow’)
8
Stop
9
Endif
10STACK(i)←P;P←LCHILD(P)
11Repeat
12Loop
13
CallVISIT(P)
//P
的左子树周游完//
14
P
←RCHILD(P)
15
If
P≠0
thenexitendif
//周游右子树//
16
Ifi=0thenreturnendif
17
P
←STACK(i);i←i-1
18
Repeat//
访问父结点//
19repeatendINORDER1
二、图的检索与周游
a)图的宽度优先检索算法
LineprocedureBFS(v)
//宽度优先检索G,它在结点v开始执行。
所有已访问结点都标上VISITED(i)=1。
图G和数组VISITED是全程量。
VISITED开始时都已置成0。
//
1
VISITED(V)←1;u
←v
2
将Q初始化库空
//Q是未检测结点的队列//
3
Loop
4
For
邻接于u的所有结点w
do
5
If
VISITED(w)=0then
callADDQ(w,Q)
//w未检
测//
6
VISITED(w)←1
7
Endif
8
Repeat
9
IfQ为空thenreturn
endif
//不再有还未检测的结点
//
10
CallDELETEQ(u,Q)
//从队中取一个未检测的结点//
11Repeat
12endBFS
b)图的宽度优先周游
ProcedureBFT(G,n)DeclareVISITED(n)
3
Fori←1tondo//将所有结点标注为未访问//
VISITED←0
Repeat
Fori←1tondo/反复调用BFS//
IfVISITED(i)=0thencallBFS(i)endif
Repeat
endBFT
c)图的深度优先检索算法procedureDFS(v)
//已知一个n结点无向(或有向)图G=(V,E)以及初值已置为零的数组VISITED(1:
n)。
这个算法访问由v可以到达的所有结点。
G和VISITED是全程量。
//
VISITED(V)←1;
For邻接于v的每个结点wdo
IfVISITED(w)=0thencallDFS(w)Endif
Repeat
endDFS
d)图的深度优先周游算法
ProcedureDFT(G,n)
DeclareVISITED(n)
Fori←1tondo//将所有结点标注为未访问//
VISITED←0
Repeat
Fori←1tondo/反复调用DFS//
IfVISITED(i)=0thencallDFS(i)endif
Repeat
endDFT
1、程序设计:
见附1
实验步骤
1、准备实验数据。
准备模拟二元树和模拟图的数据。
将二元树和模拟图的数据保存在文件中,在程序运行时读取,分别取名为
btree.txt和|Cost.txt
其中树的表现形式为第一行为根节点,其余若干行表示每行第一个数为子节
点,第二个数为父节点如:
4
图的表示方式为第一行为节点数,边数和是否为有向图,剩下的行是图和节点所组成的邻接矩阵,文件表示如下图:
其中∞表示为无穷即不可达
5
2、递归方法设计二元树周游和检索BinaryTree.java
根据算法设计的多段图向前处理法的sparks语言,写出相应的java程序,并
调试验证通过。
周游和检索的次序可以是先序、中序和后序。
(其中的为java
中的泛型类,类似c语言中的宏定义)visit()方法是访问该节点,将其加入到
一个链表中,方便以后输出,同时可以减少计算时间时噪声影响。
/**
*访问结点
*@paramnode
*/
publicvoidvisit(BinaryTreeNodenode){
list.add(node.getData());
}
/**
*递归实现二叉树的前序遍历
*@paramBT
*/
publicvoidpreOrderRecursive(BinaryTreeNodeBT){
if(BT!
=null){
visit(BT);
preOrderRecursive(BT.getLChild());
preOrderRecursive(BT.getRChild());
}
}
/**
*递归实现二叉树的中序遍历
*@paramBT
*/
publicvoidinOrderRecursive(BinaryTreeNodeBT){
if(BT!
=null){
inOrderRecursive(BT.getLChild());
visit(BT);
inOrderRecursive(BT.getRChild());
}
}
/**
*递归实现二叉树的后序遍历
6
*@paramBT
*/
publicvoidpostOrderRecursive(BinaryTreeNodeBT){
if(BT!
=null){
postOrderRecursive(BT.getLChild());
visit(BT);
postOrderRecursive(BT.getRChild());
}
}
分别将其结果保存到文件中,分析其结果
3、非递归方法设计二元树周游和检索BinaryTree.java
根据最短路径问题的动态规划程序算法的sparks语言写出对应的java程序,
并调试验证通过,对比递归和非递归程序,验证其正确性;
/**
7
*非递归实现二叉树的中序遍历
*@paramBT
*/
publicvoidinNotOrderRecursive(BinaryTreeNodeBT){
/*MyArrayStack>stack;
intmaxStackSize=50;
stack=newMyArrayStack>(maxStackSize);*/
MyLinkedStack>stack=new
MyLinkedStack>();
BinaryTreeNodep=BT;
while(p!
=null||!
stack.isEmpty()){
while(p!
=null){//找到左子树
stack.push(p);
p=p.getLChild();
}
if(!
stack.isEmpty()){
p=stack.pop();//先从栈中弹出要结点
visit(p);//再访问根结点
p=p.getRChild();//然后再指向右子树
}
}
}
输出结果保存到文件中,可以看出与递归结果一致;
4、图的宽度优先检索和周游GraphSearch.java
用邻接矩阵的方式表示整个图并将每个节点是否访问用一个数组表示,同时访
问时将该结点加入到一个链表中去,方便以后输出使用并减少噪声影响;
/**
*
图的宽度优先检索
*
G,它在结点v开始执行,所有已访问结点都标上visited[i]=1,图G和数组
visited是全程变量,visited开始记为0
*@paramv
*/
8
publicvoidBFS(intv){
visit(v);
visited[v]=1;
intu=v;
LinkedListQ=newLinkedList();//当作放置未检测结
点的队列
while(true){
LinkedListlist=G.costList[u];
for(Edgee:
list){//邻接于u的所有结点
intw=e.v;
if(visited[w]==0){
Q.add(w);//将未检测的结点为w放入队列
visit(w);//访问该结点
visited[w]=1;//将该结点标记为1
}
}
if(!
Q.isEmpty()){//若队列不为空
u=Q.remove();//将未访问的结点从队列取出赋给u,并准备下一轮循环
}else{//队列为空表示所有结点访问结束
return;
}
}
}
/**
*宽度优先周游算法
*/
publicvoidBFT(){
for(inti=1;i<=G.n;i++){//将所有结点标注为未访问//
visited[i]=0;
}
for(inti=1;iif(visited[i]==0)
BFS(i);
}
}
宽度优先检索结果(最后一行为检索的结果顺序)
9
宽度优先周游的结果:
5、图的深度度优先检索和周游GraphSearch.java
用邻接矩阵的方式表示整个图并将每个节点是否访问用一个数组表示,同时访
问时将该结点加入到一个链表中去,方便以后输出使用并减少噪声影响;
/**
*
图的深度优先检索
*
已知一个n结点的无向(或有向图)G,以及初会已置为零的数组visited,这个
算法可到达访问由v所可以到过的所有结点,G和visite是全程变量
*@paramv
*/
publicvoidDFS(intv){
visit(v);//访问该结点
visited[v]=1;//将该结点标记为1
LinkedListlist=G.costList[v];
for(Edgee:
list){//邻接于u的所有结点
intw=e.v;
if(visited[w]==0){
DFS(w);
}
}
10
}
/**
*图的深度优先周游
*/
publicvoidDFT(){
for(inti=1;i<=G.n;i++){//将所有结点标注为未访问//
visited[i]=0;
}
for(inti=1;iif(visited[i]==0)
DFS(i);
}
}
深度优先检索结果(最后一行为检索的结果顺序)
深度优先周游的结果:
11
5、【附】查找族谱中的近亲FamilyTree.java
采用二元树相同的方法将村的结果存储在文件中,并以二叉树的方式表示树的
结构,但解释与二叉树不中,其中左孩子表示子辈,右孩子表示同辈;查找是否
是近亲时首先查找该节点,将该人的父辈祖先保存在栈中,采用一般树的后序遍
历方法(即二叉树的中序周游方法)可以达到此效果,然后再比较它们在规定的
代数如5代内是否有相同的祖先,如果有则表示他们为近亲;
publicbooleanisNearRelation(myTypex,myTypey,intn){
MyLinkedStack>stack0=xStack(x);
MyLinkedStack>stack1=xStack(y);
if(stack0==null){//若有一个结点未找到
System.out.println("未找到结点"+x.data);
returnfalse;
}elseif(stack1==null){
System.out.println("未找到结点"+y.data);
returnfalse;
}
inti=0;
LinkedListarryx=newLinkedList();
LinkedListarryy=newLinkedList();
//将两个结点的父辈们组成的栈加入链表中,只取前n个
while(!
stack0.isEmpty()&&iBinaryTreeNodes=stack0.pop();
myTypem=s.getData();
arryx.add(m);
i++;
}
i=0;
while(!
stack1.isEmpty()&&iBinaryTreeNodes=stack1.pop();
myTypem=s.getData();
arryy.add(m);
i++;
}
//比较链表是否有相同元素
for(intj=0;jfor(intk=0;kif(arryx.get(j).equals(arryy.get(k))){
12
returntrue;
}
}
}
returnfalse;
}
/**
*采用后序遍历的方法对就二叉树的中序遍历方法,查找x元素,并将该结点的父辈们加
入到一个栈中
*@paramx
*@return
*/
@SuppressWarnings("unused")
publicMyLinkedStack>xStack(myTypex){
BinaryTreeNodep=familyTree.getRoot();
MyLinkedStack>stack=new
MyLinkedStack>();
while(p!
=null||!
stack.isEmpty()){
while(p!
=null){//先找到子树的最左边结点
stack.push(p);
p=p.getLChild();
}
if(!
stack.isEmpty()){
if(!
stack.isEmpty()){
p=stack.pop();//先从栈中弹出要结点
if(p.getData().equals(x)){//找到的话直接结束查找,最终返回
的是其父辈的终成的栈
returnstack;
}
p=p.getRChild();//然后再指向右子树
}
}
}//未找到返回空
returnnull;
}
族谱结构为(从1开始顺序编号)
13
表示的图形为
14
用二叉树表示如下:
比如查找F2和E8是否是5代以内的近亲,由图中可以清楚看到是,结果与之一致
15
查找它们是否为4代以内的近亲时,可以看出它是不是的
实验结果及其分析
1、递归方法设计二元树周游和检索
如果访问一个结点所需要的时间和空间为O
(1),n个结点每个结点均门访问
一次,这么算法的时间和空间复杂度均为O(n);
2、图的宽度优先检索和周游
图的宽度优先检索,t(n,e)=O(n+e)
图的宽度优先检索
BFS
n
10
20
30
40
50
60
70
80
90
100
100000次运行耗时
123
252
447
657
967
1335
1774
2276
2852
3452
16
图的宽度度优先周游,t(n)=O(n2)
图的宽度优先周游
BFT
n
10
20
30
40
50
60
70
80
90
100
100000次运行耗时
126
249
440
665
976
1349
1770
2290
2811
3477
3、图的深度优先检索和周游
图的深度度优先检索,t(n,e)=O(n+e)
图的深度优先检索
DFS
17
n
10
20
30
40
50
60
70
80
90
100
100000次运行耗时
139
180
354
634
968
1335
1749
2304
2981
3742
图的深度度优先周游,t(n)=O(n2)
图的深度优先周游
DFT
n
10
20
30
40
50
60
70
80
90
100
100000次运行耗时
174
198
363
654
967
1342
1782
2328
2993
3596
4、总结
基本的检索和周游算法主要包括对树和图各结点的遍历访问。
二元树的访问
根据每个子树的访问次序可分为前序周游,中序周游和后序周游,三种方法周游
18
所需要的时间和空间复杂度相同,但过程中在栈中所存储和结点不同,在实际情
况中可根据需要采取不同的周游方法(比如在查找近亲一