佛山科学技术学院
实验报告
课程名称数据结构
实验项目实现深度优先搜索与广度优先搜索算法
专业班级 10网络工程2 姓名蒲永毅学号 2010394223
指导教师成绩日期 2011年11月19日
一、实验目的
1、通过本实验,掌握图,无向图的基本概念,掌握图的遍历;
2、掌握图的深度优先搜索(DFS)与广度优先搜索(BFS)算法。
二、实验内容
1、建立图的存储方式;
2、图的深度优先搜索算法;
3、图的广度优先搜索算法。
三、实验原理
图的遍历是图的算法中一种非常重要的算法,通过建立图的存储结构,采用深度优先搜索与广度优先搜索算法可以进行图的遍历;
深度优先遍历是树的先根遍历的推广,是将某一条枝上的所有节点都搜索到了之后,才转向搜索另一条枝上的所有节点;
广度优先遍历与深度优先遍历的区别在于:广度优先遍历是以层为顺序,将某一层上的所有节点都搜索到了之后才向下一层搜索。
四、实验步骤
1.建立图的存储结构;
2.输入图的基本接点与信息,初始化图;
3.编写图的深度优先搜索(DFS)和广度优先搜索算法(BFS)程序;
4.采用菜单形式进行显示与选择。
5.测试数据和结果显示
(1)从键盘输入顶点数和边数;
(2)输入顶点信息;
(3)输入边的信息,以(a,b)的形式输入边的信息,构建一个无向图;
(4)对此无向图进行深度优先搜索和广度优先搜索,并输出正确的序列。
五、程序源代码及注释
/*******************************
*采用邻接表的存储结构
*构建无向图
*图的创建过程中暂不支持重复验证,
因此不能对一条边进行重复定义
******************************/
#include
#include
#include
#define MAX_VERTEX_NUM 20
typedef struct ArcNode{
int adjvex;
struct ArcNode* nextarc;
//InfoType* info;
}ArcNode;
/*********************************
*链表结点的结构用于创建栈或是队列
********************************/
typedef struct LinkNode{
ArcNode* parc; //存储指针地址
struct LinkNode* next; //指向一下个结点
}LinkNode;
typedef struct VNode{
char cData; //顶点元素值
ArcNode* firstarc; //指向第一条依附于该点的边}VNode,AdjList[MAX_VERTEX_NUM];
typedef struct {
AdjList vertices;
int vexnum; //图的当前顶点数和弧数
int arcnum;
}ALGraph;
int Visited[MAX_VERTEX_NUM];
/*********************************
*将生成的图打印出来以便确认正确性
********************************/
int PrintCheck(ALGraph* pag)
{
int i;
ArcNode* p;
printf("\nCheck the Graph!\n");
printf("No\tdata\tnext\tnext\t.....\n");
for(i=0; i
{
printf("%d\t%c\t",i,pag->vertices[i].cData);
p = pag->vertices[i].firstarc;
while(p)
{
printf("%d\t",p->adjvex);
p = p->nextarc;
}
printf("\n");
}
return 1;
}
/**************************
*采用前插法创建邻接表
*************************/
int CreateGraph(ALGraph* pag,int start,int end)
{
ArcNode* arcNodes = (ArcNode*)malloc(sizeof(ArcNode));
ArcNode* arcNodee = (ArcNode*)malloc(sizeof(ArcNode));
ArcNode* p;
if(!arcNodes || !arcNodee) return 0;
//从start->end生成关系
arcNodes->adjvex = end; //下一结点的位置
p = pag->vertices[start].firstarc;
if(!p) //第一个结点单独构造
{
arcNodes->nextarc = pag->vertices[start].firstarc;
pag->vertices[start].firstarc = arcNodes;
}
else{
while(p->nextarc) p = p->nextarc;
p->nextarc = arcNodes;
arcNodes->nextarc = NULL;
}
//end->start 的关系生成
arcNodee->adjvex = start; //下一结点的位置
p = pag->vertices[end].firstarc;
if(!p) //第一个结点单独构造
{
arcNodee->nextarc = pag->vertices[end].firstarc;
pag->vertices[end].firstarc = arcNodee;
}
else{
while(p->nextarc) p = p->nextarc;
p->nextarc = arcNodee;
arcNodee->nextarc = NULL;
}
return 1;
}
/****************************************
*深度优先遍历,非递归方式
*结点先访问再入栈
*栈的存储结构直接采用了LinkNode构成的链表
*采用前插法进行插入与删除,从而也可以完成栈的功能
*栈空条件 Stack->next == NULL
***************************************/
void DFSTraverse(ALGraph ag,int start)
{
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); //链表头结点,用做栈LinkNode* pStack = (LinkNode*)malloc(sizeof(LinkNode)); //对栈操作的指针LinkNode* temp; //临时存储
ArcNode* p;
int i;
if(!pStack||!Stack) return;
Stack->next = NULL;
p = ag.vertices[start].firstarc;
Visited[start]=1; //Flag
printf("\n输出深度优先遍历顺序:");
printf(" %c ",ag.vertices[start].cData); //访问第一个点
while(1) //正常情况下执行一次,为了打印孤立结点
{
//push stack
pStack->parc = p;
pStack->next = Stack->next; //将p接入链式栈中
Stack->next = pStack;
//push over
while(p && (Stack->next)) //当并且栈不为空时
{
while(p)
{
if(Visited[p->adjvex]) p = p->nextarc;
else
{
Visited[p->adjvex]=1;
printf(" %c ",ag.vertices[p->adjvex].cData); //Visit Function
//push stack
pStack = (LinkNode*)malloc(sizeof(LinkNode));
if(!pStack) return; //结点建立不成功
pStack->parc = p;
pStack->next = Stack->next;
Stack->next = pStack;
//push over
p = ag.vertices[p->adjvex].firstarc;
}
}
//pop stack
temp = Stack->next;
Stack->next = temp->next;
p = temp->parc->nextarc;
free(temp);
//pop over
}
for(i=0; i { if(!Visited[i]) printf(" %c ",ag.vertices[i].cData); p = ag.vertices[i].firstarc; } if(i = ag.vexnum) break; } printf("\n\n"); }; /**************************************** *广度优先遍历,非递归方式 *队列的存储结构直接采用了LinkNode构成的链表 *采用后接法进行插入,并用前插法进行删除 *从而完成队列的功能,队空条件Queue->next == NULL ***************************************/ void BFSTraverse(ALGraph ag,int start) { LinkNode* Queue = (LinkNode*)malloc(sizeof(LinkNode)); //链表头结点,用做队列 LinkNode* pQueue = (LinkNode*)malloc(sizeof(LinkNode)); //对队列操作的指针LinkNode* temp; //临时存储 LinkNode* last; //指向最后一个元素的指针 ArcNode* p; int i; if(!Queue || !pQueue) return; printf("\n输出广度优先遍历次序:"); printf(" %c ",ag.vertices[start].cData); p = ag.vertices[start].firstarc; Visited[start] = 1; while(1) //正常情况下执行一次循环 { //EnQueue pQueue->parc = p; Queue->next = pQueue; pQueue->next = NULL; last = pQueue; //指向最后一个元素的指针 //EnQueue over while(p && Queue->next) { while(p) { if(!Visited[p->adjvex]) { Visited[p->adjvex] = 1; printf(" %c ",ag.vertices[p->adjvex].cData); //Visit Function //EnQueue pQueue = (LinkNode*)malloc(sizeof(LinkNode)); if(!pQueue) return; pQueue->parc = p; pQueue->next = NULL; last->next = pQueue; last = last->next; //指向最后一个元素的指针 //EnQueue over } p = p->nextarc; } //DeQueue temp = Queue->next; p = ag.vertices[temp->parc->adjvex].firstarc; Queue ->next = temp->next; //DeQueue over } for(i=0; i { if(!Visited[i]) printf(" %c ",ag.vertices[i].cData); p = ag.vertices[i].firstarc; } if(i = ag.vexnum) break; } printf("\n\n"); } /****************************************** *主函数负责对图的初始化工作 *其中包括图的结点初始化,边初始化 *其中大部分的while(1)语句 用于验证输入数据的有效性 ******************************************/ void main() { ALGraph ag; int i,n,m; int choose; //选择遍历结点 int start,end; //边的起点与终点的位置 printf("说明: 采用邻接表的存储结构,生成无向图\n 输入数据请回车确认\n\n"); while(1) //结点个数有效性验证 { printf("请输入图的结点个数,并回车: "); scanf("%d",&n); if(n else printf("\n请注意结点个数不能大于20,并且不能为0!\n"); } ag.vexnum = n; printf("\n初始化图的结点,输入字符并回车:\n"); for(i=0; i { printf("No.%d = ",i); fflush(stdin); scanf("%c",&ag.vertices[i].cData); ag.vertices[i].firstarc = NULL; } m = (n-2)*(n+1)/2+1; //顶点数为n的图最多的边的数量为m while(1) //边的数量有效性验证 { printf("请输入边的数量: "); fflush(stdin); scanf("%d",&i); if(i<=m && i>=0) break; else printf("\n请注意边的数量不能大于%d,并且不能小于1!\n",m); } ag.arcnum = i; printf("\n初始化图的边,结点从0开始计,最大为%d\n",n-1); for(i=1; i<=ag.arcnum; i++) { while(1) //起点有效性验证 { printf("第<%d>条边的起点: ",i); fflush(stdin); scanf("%d",&start); if(start else printf("重新输入 "); } while(1) //终点有效性验证 { printf("第<%d>条边的终点: ",i); fflush(stdin); scanf("%d",&end); if(end else printf("重新输入 "); } printf("\n"); CreateGraph(&ag,start,end); } PrintCheck(&ag); //打印出生成的图 printf("\n开始进行图的遍历!\n"); while(1) //起始点有效性验证 { printf("请输入深度优先遍历的开始结点:"); fflush(stdin); scanf("%d",&choose); if(choose>=0 && choose else printf("重新输入 "); } DFSTraverse(ag,choose); //深度优先遍历 i = 0; //重新初始化Visited数组 while(Visited[i]!='\0') { Visited[i] = 0; i++; } while(1) //起始点有效性验证 { printf("请输入广度优先遍历的开始结点:"); fflush(stdin); scanf("%d",&choose); if(choose>=0 && choose else printf("重新输入 "); } BFSTraverse(ag,choose); //广度优先遍历 system("pause"); } 程序运行截图 六、实验体会 相对于第二个实验,实验三实现深度优先搜索和广度优先搜索算法的难度大了许多,对理论课基础的要求比较高,图这种数据结构是我的薄弱之处,隐刺刚开始我就感觉到无处下手,在请教同学后,慢慢地深入,才一步步的把这个问题解决,在之后更要多加练习,巩固这方面的知识。 一、实验目的 1.掌握图的各种存储结构,特别要熟练掌握邻接矩阵和邻接表存储结构; 2.遍历是图各种应用的算法的基础,要熟练掌握图的深度优先遍历和宽度优先遍历算法,复习栈和队列的应用; 3.掌握图的各种应用的算法:图的连通性、连通分量和最小生成树、拓扑排序、关键路径。 二、实验内容 实验内容1**图的遍历 [问题描述] 许多涉及图上操作的算法都是以图的遍历为基础的。写一个程序,演示在连通无向图上遍历全部顶点。 [基本要求] 建立图的邻接表的存储结构,实现无向图的深度优先遍历和广度优先遍历。以用户指定的顶点为起点,分别输出每种遍历下的顶点访问序列。 [实现提示] 设图的顶点不超过30个,每个顶点用一个编号表示(如果一个图有N个顶点,则它们的编号分别为1,2,…,N)。通过输入图的全部边输入一个图,每条边是两个顶点编号对,可以对边依附顶点编号的输入顺序作出限制(例如从小到大)。 [编程思路] 首先图的创建,采用邻接表建立,逆向插入到单链表中,特别注意无向是对称插入结点,且要把输入的字符在顶点数组中定位(LocateVex(Graph G,char *name),以便后来的遍历操作,深度遍历算法采用递归调用,其中最主要的是NextAdjVex(Graph G, int v, int w);FirstAdjVex ()函数的书写,依次递归下去,广度遍历用队列的辅助。 [程序代码] 头文件: #include 合肥学院 计算机科学与技术系 课程设计报告 2013~2014学年第二学期 课程数据结构与算法 课程设计名称图的深度优先遍历算法的实现 学生姓名陈琳 学号1204091022 专业班级软件工程 指导教师何立新 2014 年9 月 一:问题分析和任务定义 涉及到数据结构遍会涉及到对应存储方法的遍历问题。本次程序采用邻接表的存储方法,并且以深度优先实现遍历的过程得到其遍历序列。 深度优先遍历图的方法是,从图中某顶点v 出发: (1)访问顶点v ; (2)依次从v 的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v 有路径相通的顶点都被访问; (3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。 二:数据结构的选择和概要设计 设计流程如图: 图1 设计流程 利用一维数组创建邻接表,同时还需要一个一维数组来存储顶点信息。之后利用创建的邻接表来创建图,最后用深度优先的方法来实现遍历。 图 2 原始图 1.从0开始,首先找到0的关联顶点3 2.由3出发,找到1;由1出发,没有关联的顶点。 3.回到3,从3出发,找到2;由2出发,没有关联的顶点。 4.回到4,出4出发,找到1,因为1已经被访问过了,所以不访问。 所以最后顺序是0,3,1,2,4 三:详细设计和编码 1.创建邻接表和图 void CreateALGraph (ALGraph* G) //建立邻接表函数. { int i,j,k,s; char y; EdgeNode* p; //工作指针. printf("请输入图的顶点数n与边数e(以逗号做分隔符):\n"); scanf("%d,%d",&(G->n),&(G->e)); scanf("%c",&y); //用y来接收回车符. for(s=0;s 广度优先搜索训练题 一、奇怪的电梯 源程序名LIFT.PAS 可执行文件名 LIFT.EXE 输入文件名 LIFT.IN 输出文件名 LIFT.OUT 呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第i层楼(1<=i<=N)上有一个数字Ki(0<=Ki<=N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3 3 1 2 5代表了Ki(K1=3,K2=3,……),从一楼开始。在一楼,按“上”可以到4楼,按“下”是不起作用的,因为没有-2楼。那么,从A楼到B楼至少要按几次按钮呢? 输入 输入文件共有二行,第一行为三个用空格隔开的正整数,表示N,A,B(1≤N≤200, 1≤A,B≤N),第二行为N个用空格隔开的正整数,表示Ki。 输出 输出文件仅一行,即最少按键次数,若无法到达,则输出-1。 样例 LIFT.IN 5 1 5 3 3 1 2 5 LIFT.OUT 3 二、字串变换 [问题描述]: 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为:在 A$中的子串 A1$ 可以变换为 B1$、A2$ 可以变换为B2$ …。例如:A$='abcd' B$='xyz' 变换规则为: ‘abc’->‘xu’‘ud’->‘y’‘y’->‘yz’ 则此时,A$ 可以经过一系列的变换变为 B$,其变换的过程为:‘abcd’->‘xud’->‘xy’->‘xyz’ 共进行了三次变换,使得 A$ 变换为B$。 [输入]: 键盘输人文件名。文件格式如下: A$ B$ A1$ B1$ \ 一.实验目的 熟悉图的存储结构,掌握用单链表存储数据元素信息和数据元素之间的关系的信息的方法,并能运用图的深度优先搜索遍历一个图,对其输出。 二.实验原理 深度优先搜索遍历是树的先根遍历的推广。假设初始状态时图中所有顶点未曾访问,则深度优先搜索可从图中某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有与v有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。 图的邻接表的存储表示: #define MAX_VERTEX_NUM 20 #define MAXNAME 10 typedef char VertexType[MAXNAME]; typedef struct ArcNode{ int adjvex; struct ArcNode *nextarc; }ArcNode; typedef struct VNode{ VertexType data; ArcNode *firstarc; }VNode,AdjList[MAX_VERTEX_NUM]; typedef struct{ AdjList vertices; int vexnum,arcnum; int kind; }ALGraph; 三.实验内容 编写LocateVex函数,Create函数,print函数,main函数,输入要构造的图的相关信息,得到其邻接表并输出显示。 四。实验步骤 1)结构体定义,预定义,全局变量定义。 #include"stdio.h" #include"stdlib.h" #include"string.h" #define FALSE 0 #define TRUE 1 #define MAX 20 typedef int Boolean; #define MAX_VERTEX_NUM 20 习题 3 1、答:深度优先搜索算法的特点是 ①一般不能保证找到最优解; ②当深度限制不合理时,可能找不到解,可以将算法改为可变深度限制; ③方法与问题无关,具有通用性; ④属于图搜索方法。 宽度优先搜索算法的特点是 ①当问题有解时,一定能找到解; ②当问题为单位耗散值,并且问题有解时,一定能找到最优解; ③效率低; ④方法与问题无关,具有通用性; ⑤属于图搜索方法。 2、答:在决定生成子状态的最优次序时,应该采用深度进行衡量,使深度大的 结点优先扩展。 3、答:(1)深度优先 (2)深度优先 (3)宽度优先 (4)宽度优先 (5)宽度优先 4、答:如果把一个皇后放在棋盘的某个位置后,它所影响的棋盘位置数少,那 么给以后放皇后留下的余地就大,找到解的可能性也大;反之留下的余地就小,找到解的可能性也小。 并不是任何启发函数对搜索都是有用的。 6、讨论一个启发函数h在搜索期间可以得到改善的几种方法。 7、答:最短路径为ACEBDA,其耗散值为15。 8、解:(1)(S,O,S0,G) S:3个黑色板和3个白色板在7个空格中的任何一种布局都是一个状态。 O:①一块板移入相邻的空格; ②一块板相隔1块其他的板跳入空格; ③一块板相隔2块其他的板跳入空格。 S0: B B B W W W G: W W W B B B W W W B B B W W W B B B W W W B B B W W W B B B W W W B B B W W W B B B (2)1401231231234567333377 =???????????=?P P P (3)定义启发函数h 为每一白色板左边的黑色板数的和。 显然,)()(n h n h *≤,所以该算法具有可采纳性。 又,?? ?≤-=),()()(0)(j i i j n n c n h n h t h ,所以该启发函数h 满足单调限制条件。 9、解: ((( ),( )),( ),(( ),( ))) ((S,( )),( ),(( ),( ))) ((A,( )),( ),(( ),( ))) ((A,S),( ),(( ),( ))) ((A,A),( ),(( ),( ))) ((A),( ),(( ),( ))) (S,( ),(( ),( ))) (A,( ),(( ),( ))) (A,S,(( ),( ))) (A,A,(( ),( ))) (A,(( ),( ))) *问题描述: 建立图的存储结构(图的类型可以是有向图、无向图、有向网、无向网,学生可以任选两种类型),能够输入图的顶点和边的信息,并存储到相应存储结构中,而后输出图的邻接矩阵。 1、邻接矩阵表示法: 设G=(V,E)是一个图,其中V={V1,V2,V3…,Vn}。G的邻接矩阵是一个他有下述性质的n阶方阵: 1,若(Vi,Vj)∈E 或 若图中每个顶点只含一个编号i(1≤i≤vnum),则只需一个二维数组表示图的邻接矩阵。此时存储结构可简单说明如下: type adjmatrix=array[1..vnum,1..vnum]of adj; 利用邻接矩阵很容易判定任意两个顶点之间是否有边(或弧)相联,并容易求得各个顶点的度。 对于无向图,顶点Vi的度是邻接矩阵中第i行元素之和,即 n n D(Vi)=∑A[i,j](或∑A[i,j]) j=1 i=1 对于有向图,顶点Vi的出度OD(Vi)为邻接矩阵第i行元素之和,顶点Vi 的入度ID(Vi)为第i列元素之和。即 n n OD(Vi)=∑A[i,j],OD(Vi)=∑A[j,i]) j=1j=1 用邻接矩阵也可以表示带权图,只要令 Wij, 若 有两种常用的方法可用来搜索图:即深度优先搜索和广度优先搜索。它们最终都会到达所有 连通的顶点。深度优先搜索通过栈来实现,而广度优先搜索通过队列来实现。 深度优先搜索: 深度优先搜索就是在搜索树的每一层始终先只扩展一个子节点,不断地向纵深前进直到不能再前进(到达叶子节点或受到深度限制)时,才从当前节点返回到上一级节点,沿另一方向又继续前进。这种方法的搜索树是从树根开始一枝一枝逐渐形成的。 下面图中的数字显示了深度优先搜索顶点被访问的顺序。 "* ■ J 严-* 4 t C '4 --------------------------------- --- _ 为了实现深度优先搜索,首先选择一个起始顶点并需要遵守三个规则: (1) 如果可能,访问一个邻接的未访问顶点,标记它,并把它放入栈中。 (2) 当不能执行规则1时,如果栈不空,就从栈中弹出一个顶点。 (3) 如果不能执行规则1和规则2,就完成了整个搜索过程。 广度优先搜索: 在深度优先搜索算法中,是深度越大的结点越先得到扩展。如果在搜索中把算法改为按结点的层次进行搜索,本层的结点没有搜索处理完时,不能对下层结点进行处理,即深度越小的结点越先得到扩展,也就是说先产生的结点先得以扩展处理,这种搜索算法称为广度优先搜索法。 在深度优先搜索中,算法表现得好像要尽快地远离起始点似的。相反,在广度优先搜索中, 算法好像要尽可能地靠近起始点。它首先访问起始顶点的所有邻接点,然后再访问较远的区 域。它是用队列来实现的。 下面图中的数字显示了广度优先搜索顶点被访问的顺序。 实现广度优先搜索,也要遵守三个规则: ⑴ 访问下一个未来访问的邻接点,这个顶点必须是当前顶点的邻接点,标记它,并把它插入到队列中。(2)如果因为已经没有未访问顶点而不能执行规则1 DFS与BFS的比较 姓名:班级:学号: 一、图的遍历 1.图的遍历的含义 图的遍历是指从图中某结点出发,按某既定方式访问图中各个可访问到的结点,使每个可访问到的结点恰被访问一次。 2.图的遍历方式:深度优先与广度优先 二、DFS与BFS的区别 1.概念 深度优先遍历可定义如下:首先访问出发点v,并将其标记为已访问过;然后依次从v出发搜索v的每个邻接点w。若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有路径相通的顶点(亦称为从源点可达的顶点)均已被访问为止。若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问止。 广度优先遍历可定义如下:假设从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先与“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。 2. 路径 深度优先就是,从初始点出发,不断向前走,如果碰到死路了,就往回走一步,尝试另一条路,直到发现了目标位置。这种方法,即使成功也不一定找到一条好路,但是需要记住的位置比较少。 广度优先就是,从初始点出发,把所有可能的路径都走一遍,如果里面没有目标位置,则尝试把所有两步能够到的位置都走一遍,看有没有目标位置;如果还不行,则尝试所有三步可以到的位置。这种方法,一定可以找到一条最短路径,但需要记忆的内容实在很多,要量力而行。 3.算法实现 (1) 图的深度优先算法的一般性描述: long DFS(图s,结点v。) { // 从结点v。出发,深度优先遍历图s,返回访问到的结点总数 int nNodes; //寄存访问到的结点数目 访问v。; 图的广度优先搜索的应用 ◆内容提要 广度优先搜索是分层次搜索,广泛应用于求解问题的最短路径、最少步骤、最优方法等方面。本讲座就最短路径问题、分酒问题、八数码问题三个典型的范例,从问题分析、算法、数据结构等多方面进行了讨论,从而形成图的广度优先搜索解决问题的模式,通过本讲座的学习,能明白什么样的问题可以采用或转化为图的广度优先搜索来解决。在讨论过程中,还同时对同一问题进行了深层次的探讨,进一步寻求解决问题的最优方案。 ◆知识讲解和实例分析 和深度优先搜索一样,图的广度优先搜索也有广泛的用途。由于广度优先搜索是分层次搜索的,即先将所有与上一层顶点相邻接的顶点搜索完之后,再继续往下搜索与该层的所有邻接而又没有访问过的顶点。故此,当某一层的结点出现目标结点时,这时所进行的步骤是最少的。所以,图的广度优先搜索广泛应用于求解问题的最短路径、最少步骤、最优方法等方面。 本次讲座就几个典型的范例来说明图的广度优先搜索的应用。 先给出图的广度优先搜索法的算法描述: F:=0;r:=1;L[r]:=初始值; H:=1;w:=1;bb:=true; While bb do begin H:=h+1;g[h]:=r+1; For I:=1 to w do Begin F:=f+1; For t:=1 to 操作数do Begin ⑴m:=L[f]; {出队列}; ⑵判断t操作对m结点的相邻结点进行操作;能则设标记bj:=0,并生成新结点;不能,则设标记bj:=1; if bj:=0 then {表示有新结点生成} begin for k:=1 to g[h]-1 do if L[k]=新结点then {判断新扩展的结点是否以前出现过} begin bj:=1;k:=g[h]-1 题目: 图的深度优先遍历算法 一、实验题目 前序遍历二叉树 二、实验目的 ⑴掌握图的逻辑结构; ⑵掌握图的邻接矩阵存储结构; ⑶验证图的邻接矩阵存储及其深度优先遍历操作的实现。 三、实验内容与实现 ⑴建立无向图的邻接矩阵存储; ⑵对建立的无向图,进行深度优先遍历;实验实现 #include typedef struct EdgeNode { int adjvex; struct EdgeNode *next; }EdgeNode; typedef struct VertexNode { VertexType data; EdgeNode *firstedge; }VertexNode,AdjList[MaxVex]; typedef struct Graph{ AdjList adjList; int numVertexes,numEdges; }Graph,*GraphAdjList; typedef struct LoopQueue{ int data[MaxVex]; int front,rear; }LoopQueue,*Queue; void initQueue(Queue &Q){ Q->front=Q->rear=0; } Bool QueueEmpty(Queue &Q){ if(Q->front == Q->rear){ return TRUE; }else{ return FALSE; } } Bool QueueFull(Queue &Q){ if((Q->rear+1)%MaxVex == Q->front){ return TRUE; }else{ return FALSE; } } void EnQueue(Queue &Q,int e){ if(!QueueFull(Q)){ Q->data[Q->rear] = e; //算法6.7广度优先搜索遍历连通图 #include 基于C语言的广度优先搜素算法的实现 1.算法说明 广度优先搜索使用队列(queue)来实现,整个过程也可以看做一个倒立的树形: (1)把根节点放到队列的末尾。 (2)每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。 (3)找到所要找的元素时结束程序。 (4)如果遍历整个树还没有找到,结束程序。 本次算法的应用中,我用这个队列来保存最短路径。首先我定义队列为“真进假出”,所谓“真进”就是当添加一个元素时,把元素放置队尾,然后rear++, 而“假出”就是当删除一个元素时,并没有真的删除队首元素,只是front++。 通过比较搜索所得的所有可行路径的长度,这样我们就可以从队列中获取一条最短路径! 2.代码及结果 #include 天津理工大学实验报告学院(系)名称:计算机与通信工程学院 实验思路: 首先,定义邻接矩阵和图的类型,定义循环队列来存储,本程序中只给出了有向图的两种遍历,定义深度优先搜索和广度优先搜索的函数,和一些必要的函数,下面的程序中会有说明,然后是函数及运行结果! #include 华北水利水电学院数据结构实验报告 20 10 ~20 11 学年第一学期2008级计算机专业 班级:107学号:200810702姓名:王文波 实验四图的应用 一、实验目的: 1.掌握图的存储结构及其构造方法 2.掌握图的两种遍历算法及其执行过程 二、实验内容: 以邻接矩阵或邻接表为存储结构,以用户指定的顶点为起始点,实现无向连通图的深度优先及广度优先搜索遍历,并输出遍历的结点序列。 提示:首先,根据用户输入的顶点总数和边数,构造无向图,然后以用户输入的顶点为起始点,进行深度优先和广度优先遍历,并输出遍历的结果。 三、实验要求: 1.各班学号为单号的同学采用邻接矩阵实现,学号为双号的同学采用邻接表实现。 2.C/ C++完成算法设计和程序设计并上机调试通过。 3.撰写实验报告,提供实验结果和数据。 4.写出算法设计小结和心得。 四、程序源代码: #include { int nData; QueueNode* next; }; struct QueueList { QueueNode* front; QueueNode* rear; }; void EnQueue(QueueList* Q,int e) { QueueNode *q=new QueueNode; q->nData=e; q->next=NULL; if(Q==NULL) return; if(Q->rear==NULL) Q->front=Q->rear=q; else { Q->rear->next=q; Q->rear=Q->rear->next; } } void DeQueue(QueueList* Q,int* e) { if (Q==NULL) return; if (Q->front==Q->rear) { *e=Q->front->nData; Q->front=Q->rear=NULL; } else { *e=Q->front->nData; Q->front=Q->front->next; } } //创建图 void CreatAdjList(Graph* G) { int i,j,k; edgenode* p1; edgenode* p2; *问题描述: 建立图的存储结构,能够输入图的顶点和边的信息,并存储到相应存储结构中,而后输出图的邻接矩阵。 1、邻接矩阵表示法: 设G=(V,E)是一个图,其中V={V1,V2,V3…,Vn}。G的邻接矩阵是一个他有下述性质的n阶方阵: 1,若(Vi,Vj)∈E 或 对于有向图,顶点Vi的出度OD(Vi)为邻接矩阵第i行元素之和,顶点Vi 的入度ID(Vi)为第i列元素之和。即 n n OD(Vi)=∑A[i,j],OD(Vi)=∑A[j,i]) j=1j=1 用邻接矩阵也可以表示带权图,只要令 Wij, 若 算法设计:深度优先遍历和广度优先遍历实现 深度优先遍历过程 1、图的遍历 和树的遍历类似,图的遍历也是从某个顶点出发,沿着某条搜索路径对图中每个顶点各做一次且仅做一次访问。它是许多图的算法的基础。 深度优先遍历和广度优先遍历是最为重要的两种遍历图的方法。它们对无向图和有向图均适用。 注意: 以下假定遍历过程中访问顶点的操作是简单地输出顶点。 2、布尔向量visited[0..n-1]的设置 图中任一顶点都可能和其它顶点相邻接。在访问了某顶点之后,又可能顺着某条回路又回到了该顶点。为了避免重复访问同一个顶点,必须记住每个已访问的顶点。为此,可设一布尔向量visited[0..n-1],其初值为假,一旦访问了顶点Vi之后,便将visited[i]置为真。 -------------------------- 深度优先遍历(Depth-First Traversal) 1.图的深度优先遍历的递归定义 假设给定图G的初态是所有顶点均未曾访问过。在G中任选一顶点v为初始出发点(源点),则深度优先遍历可定义如下:首先访问出发点v,并将其标记为已访问过;然后依次从v出发搜索v的每个邻接点w。若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有路径相通的顶点(亦称为从源点可达的顶点)均已被访问为止。若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问为止。 图的深度优先遍历类似于树的前序遍历。采用的搜索方法的特点是尽可能先对纵深方向进行搜索。这种搜索方法称为深度优先搜索(Depth-First Search)。相应地,用此方法遍历图就很自然地称之为图的深度优先遍历。 2、深度优先搜索的过程 设x是当前被访问顶点,在对x做过访问标记后,选择一条从x出发的未检测过的 #include 广度优先搜索实例 【例题】八数码难题(Eight-puzzle)。在3X3的棋盘上,摆有 8个棋子,在每个棋子上标有1~8中的某一数字。棋盘中留有一个空格。空格周围的棋子可以移到空格中。要求解的问题是,给出一种初始布局(初始状态)和目标布局(目标状态),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。初始状态和目标状态如下: 初始状态目标状态 2 8 3 1 2 3 1 6 4 8 4 7 5 7 6 5 求解本题我们可以分3步进行。 问题分析: 由的解于题目要找是达到目标的最少步骤,因此可以这样来设计解题的方法: 初始状态为搜索的出发点,把移动一步后的布局全部找到,检查是否有达到目标的布局,如果没有,再从这些移动一步的布局出发,找出移动两步后的所有布局,再判断是否有达到目标的。依此类推,一直到某布局为目标状态为止,输出结果。由于是按移动步数从少到多产生新布局的,所以找到的第一个目标一定是移动步数最少的一个,也就是最优解。 建立产生式系统: (1) 综合数据库。用3X3的二维数组来表示棋盘的布局比较直观。我们用Ch[i,j]表示第i 行第j列格子上放的棋子数字,空格则用0来表示。为了编程方便,还需存储下面3个数据:该布局的空格位置(Si,Sj);初始布局到该布局的步数,即深度dep;以及该布局的上一布局,即父结点的位置(pnt)。这样数据库每一个元素应该是由上述几个数据组成的记录。 在程序中,定义组成数据库元素的记录型为: Type node=record ch:array[1..3,1..3] of byte;{存放某种棋盘布局} si,sj:byte; {记录此布局中空格位置} dep,pnt:byte; end; 因为新产生的结点深度(从初始布局到该结点的步数)一般要比数据库中原有的结点深度大(或相等)。按广度优先搜索的算法,深度大(步数多)的结点后扩展,所以新产生的结点应放在数据库的后面。而当前扩展的结点从数据库前面选取,即处理时是按结点产生的先后次序进行扩展。这样广度优先搜索的数据库结构采用队列的结构形式较合适。我们用记录数组data来表示数据库,并设置两个指针:Head为队列的首指针,tail为队列的尾指针。(2) 产生规则。原规则规定空格周围的棋子可以向空格移动。但如果换一种角度观察,也可看作空格向上、下、左、右4个位置移动,这样处理更便于编程。设空格位置在(Si,sj),则有4条规则: ①空格向上移动: if si-1>=1 then ch[si,sj]:=ch[si-1,sj];ch[si-1,sj]:=0 ②空格向下移动: if si+1<=3 then [si,sj]:=ch[si+1,sj];ch[si+1,sj]:=0 宽度优先搜索算法 1.概述: 宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。 之所以称之为宽度优先算法,是因为算法自始至终一直通过已找到和未找到顶点之间的边界向外扩展,就是说,算法首先搜索和s距离为k的所有顶点,然后再去搜索和S距离为k+l的其他顶点。 例题hdu 1242 https://www.wendangku.net/doc/e014428852.html,/showproblem.php?pid=1242 https://www.wendangku.net/doc/e014428852.html,/zhuihunmiling/article/details/897 9570(DFS做法) 这一此我们介绍广度优先搜索 按照惯例,我们还是先说一下该题目的主要易错点 1.天使可能有多个朋友,所以不能从朋友的位置开始着天使,只能是从天使找离他最近的朋友 2.题目要求的是找到一个用时最少的朋友,而不是步数最少 既然是广度优先,那么必然用到队列,但是队列只能是先进先出,即是步数少的先遍历到,显然是不符合题目要求的,那应该怎么办呢? c++给我们提供了标准的函数库,可以引入#include 图的深度广度优先遍历操作代码
图的深度优先遍历算法课程设计报告
广度优先搜索训练题
图的深度优先遍历实验报告
答深度优先搜索算法的特点是
邻接矩阵表示图深度广度优先遍历
广度优先搜索和深度优先搜索
深度优先算法与广度优先算法的比较
图的广度优先搜索的应用
数据结构实验报告图的深度优先遍历算法
算法6.7-广度优先搜索遍历连通图
基于C语言的广度优先搜索
数据结构实验四图的深度优先与广度优先遍历
图的深度优先遍历和广度优先遍历
邻接矩阵表示图_深度_广度优先遍历
算法设计:深度优先遍历和广度优先遍历
图的深度优先搜索,广度优先搜索,代码
广度优先搜索 实例
广度优先搜索(Breadth-First-Search)