实验四:图地深度优先与广度优先遍历
图的深度广度优先遍历操作代码

一、实验目的1.掌握图的各种存储结构,特别要熟练掌握邻接矩阵和邻接表存储结构;2.遍历是图各种应用的算法的基础,要熟练掌握图的深度优先遍历和宽度优先遍历算法,复习栈和队列的应用;3.掌握图的各种应用的算法:图的连通性、连通分量和最小生成树、拓扑排序、关键路径。
二、实验内容实验内容1**图的遍历[问题描述]许多涉及图上操作的算法都是以图的遍历为基础的。
写一个程序,演示在连通无向图上遍历全部顶点。
[基本要求]建立图的邻接表的存储结构,实现无向图的深度优先遍历和广度优先遍历。
以用户指定的顶点为起点,分别输出每种遍历下的顶点访问序列。
[实现提示]设图的顶点不超过30个,每个顶点用一个编号表示(如果一个图有N个顶点,则它们的编号分别为1,2,…,N)。
通过输入图的全部边输入一个图,每条边是两个顶点编号对,可以对边依附顶点编号的输入顺序作出限制(例如从小到大)。
[编程思路]首先图的创建,采用邻接表建立,逆向插入到单链表中,特别注意无向是对称插入结点,且要把输入的字符在顶点数组中定位(LocateVex(Graph G,char *name),以便后来的遍历操作,深度遍历算法采用递归调用,其中最主要的是NextAdjVex(Graph G, int v, int w);FirstAdjVex ()函数的书写,依次递归下去,广度遍历用队列的辅助。
[程序代码]头文件:#include<stdio.h>#include<stdlib.h>#define MAX_VERTEX_NUM 30#define MAX_QUEUE_NUMBER 30#define OK 1#define ERROR 0#define INFEASIBLE -1#define OVERFLOW -2#define TRUE 1#define FALSE 0typedef int Status;typedef int InfoType;typedef int Status;/* 定义弧的结构*/typedef struct ArcNode{int adjvex; /*该边所指向的顶点的位置*/ struct ArcNode *nextarc; /*指向下一条边的指针*/ InfoType info; /*该弧相关信息的指针*/}ArcNode;/*定义顶点的结构*/typedef struct VNode{char data[40]; /*顶点信息*/ArcNode *firstarc; /*指向第一条依附该顶点的弧的指针*/}VNode,AdjList[MAX_VERTEX_NUM];/*定义图的结构*/typedef struct {AdjList vertices;int vexnum,arcnum; /*图的当前顶点数和弧数*/int kind; /*图的类型标志*/}Graph;/*定义队列的结构*/typedef struct{int *elem;int front, rear;}Queue;/*功能选择*/void MenuSelect(int w);/*顶点定位*/int LocateVex(Graph G,char *name);/*创建无向图*/void CreateGraph(Graph &G);/*求第一个顶点*/int FirstAdjVex(Graph G, int v);/*求下一个顶点*/int NextAdjVex(Graph G, int v, int w);/*深度递归*/void DFS(Graph G, int v) ;/*深度遍历*/void DFSTravel(Graph G,int v);/*广度遍历*/void BFSTraverse(Graph G,char *name);/*初始化队列*/Status InitQueue(Queue &Q);/*判空*/Status EmptyQueue(Queue Q);/*进队*/Status EnQueue(Queue &Q, int e);/*出队*/Status DeQueue(Queue &Q, int &e);实现文件:#include <stdio.h>#include"malloc.h"#include "tuhead.h"#include "stdlib.h"#include "string.h"bool visited[MAX_VERTEX_NUM];/************************************************************ 顶点定位************************************************************/int LocateVex(Graph G,char *name){int i;for(i=1;i<=G.vexnum;i++) //从1号位置开始存储if(strcmp(name,G.vertices[i].data)==0) //相等则找到,返回位序return i;return -1;}/************************************************************ 创建无向图************************************************************/void CreateGraph(Graph &G){ArcNode *p;char name1[10],name2[10];int i,j,k;printf(" 请输入顶点数,按回车键结束:");scanf("%d",&G.vexnum);printf(" 请输入弧数,按回车键结束:");scanf("%d",&G.arcnum);printf(" 请依次输入顶点名(用空格分开且字符小于10),按回车键结束:\n");printf(" ");for(i=1;i<=G.vexnum;i++) //从1号位置开始存储{scanf("%s",G.vertices[i].data); //从一号位置开始初始化G.vertices[i].firstarc=NULL;}printf("\n\n\n\n");printf(" ………………………………………输入小提示………………………………………\n");printf(" &&&&1 为避免输入遗漏,最好从选择任意一点,输入所有相邻边\n");printf(" &&&&2 输入边时格式(用空格分开,即格式为顶点(空格)顶点(空格))\n");printf(" ………………………………………输入小提示………………………………………\n\n\n\n");for(k=0;k<G.arcnum;k++){printf("请输入相邻的两个顶点,按回车键结束:");scanf("%s%s",name1,name2);i=LocateVex(G,name1); //返回位序j=LocateVex(G,name2);p=(ArcNode *)malloc(sizeof(ArcNode)); //申请边节点p->adjvex=j; //插入到邻接表中,注意此处为逆向插入到单链表中p->nextarc=G.vertices[i].firstarc;G.vertices[i].firstarc=p;//无向图,注意是对称插入结点p=(ArcNode *)malloc(sizeof(ArcNode));p->adjvex=i;p->nextarc=G.vertices[j].firstarc;G.vertices[j].firstarc=p;}}/************************************************************ 求第一个顶点************************************************************/int FirstAdjVex(Graph G, int v){ArcNode *p;if(v>=1 && v<=G.vexnum){p=G.vertices[v].firstarc;if(p->nextarc==NULL)return 0;elsereturn (p->nextarc->adjvex); //返回第一个顶点字符}return -1;}/************************************************************ 求下一个顶点************************************************************/int NextAdjVex(Graph G, int v, int w){ //在图G中寻找第v个顶点的相对于w的下一个邻接顶点ArcNode *p;if(v>=1 && v<=G.vexnum && w>=1 && w<=G.vexnum){p=G.vertices[v].firstarc;while(p->adjvex!=w)p=p->nextarc; //在顶点v的弧链中找到顶点wif(p->nextarc!=NULL)return 0; //若已是最后一个顶点,返回0 elsereturn(p->nextarc->adjvex); //返回下一个邻接顶点的序号}return -1;}/************************************************************ 深度递归************************************************************/void DFS(Graph G, int v){int w;ArcNode *p;visited[v]=1;printf("%s ",G.vertices[v].data); //访问第v个顶点p=G.vertices[v].firstarc; //p为依附顶点的第一条边while (p!=NULL){w=p->adjvex;if(visited[w]==0)DFS(G,w);p=p->nextarc; //下移指针}}/************************************************************ 深度遍历************************************************************/void DFSTravel(Graph G,int v){for(int i=1;i<=G.vexnum;i++)visited[i]=0;int w;ArcNode *p;visited[v]=1;printf("%s ",G.vertices[v].data); //访问第v个顶点p=G.vertices[v].firstarc;while (p!=NULL){w=p->adjvex;if(visited[w]==0)DFS(G,w);p=p->nextarc;}}/************************************************************ 初始化队列************************************************************/Status InitQueue(Queue &Q){Q.elem = new int[MAX_QUEUE_NUMBER];Q.front = Q.rear = 0;return OK;}Status EmptyQueue(Queue Q){if(Q.front==Q.rear)return 0;elsereturn 1;}/*********************************************************** * 进队列* ***********************************************************/ Status EnQueue(Queue &Q, int e){if((Q.rear + 1)%MAX_QUEUE_NUMBER != Q.front)Q.elem[Q.rear ] = e;else ;Q.rear = (Q.rear + 1)%MAX_QUEUE_NUMBER;return OK;}/*********************************************************** * 出队列* ***********************************************************/ Status DeQueue(Queue &Q, int &e){if(Q.rear != Q.front)e = Q.elem[Q.front];else ;Q.front = (Q.front+1)%MAX_QUEUE_NUMBER;return OK;}/*********************************************************** * 广度遍历************************************************************/void BFSTraverse(Graph G,char *name){ArcNode *p;int v,w,u,k=0;Queue Q;int visited[20];for(v=1;v<=G.vexnum;v++) //初始化visited[v]=0;InitQueue(Q);for(v=LocateVex(G,name);k!=2;v=(v+1)%(G.vexnum-1)) //v为输入的字符转化的位序{if(v+1==LocateVex(G,name)) //从v开始走完图的所有顶点k++;if(visited[v]==0){visited[v]=1;printf("%s ",G.vertices[v].data); //访问第v个顶点EnQueue(Q,v); // 进队while(EmptyQueue(Q)!=0){DeQueue(Q,u); //出队p=G.vertices[u].firstarc;while(p!=NULL){w=p->adjvex; //p边的下一个顶点if(visited[w]==0){printf("%s ",G.vertices[w].data);visited[w]=1;EnQueue(Q,w);}p=p->nextarc; //下移指针}}}}}主文件:#include <stdio.h>#include"malloc.h"#include "tuhead.h"#include "stdlib.h"#include "string.h"/************************************************************ 界面控制************************************************************/void main(){printf("\n################################# 图的遍历#################################\n");printf("\n $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");printf("\n");printf(" 1 ------- 图的创建\n");printf(" 2 ------- 图的深度优先遍历\n");printf(" 3 ------- 图的广度优先遍历\n");printf(" 0 ------- 退出\n");printf("\n $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");printf("\n");printf("请输入选择的操作代码(0-3)按回车键结束\n");MenuSelect(1);}/************************************************************ 功能选择************************************************************/void MenuSelect(int w){int select,done;int v;Graph G; char name[10];while (done) {printf("input the operating code : ");scanf("%d",&select);switch(select){case 1: printf("根据要求创建图:\n ");CreateGraph(G);break;case 2: printf("请输入深度优先遍历开始点的名:");scanf("%s",name);v=LocateVex(G,name); //将输入字符找到在顶点数组name对应的序号Vprintf("深度优先遍历:");DFSTravel(G,v);printf("\n");break;case 3: printf("请输入广度优先遍历开始点的名:");scanf("%s",name);printf("广度优先遍历:");BFSTraverse(G,name);printf("\n");break;case 0: done=0; break;default: printf(" ERROR\n");}printf("\n");}}[实验数据与结果]测试数据:实验结果。
深度优先算法和广度优先算法的时间复杂度

深度优先算法和广度优先算法的时间复杂度深度优先算法和广度优先算法是在图论中常见的两种搜索算法,它们在解决各种问题时都有很重要的作用。
本文将以深入浅出的方式从时间复杂度的角度对这两种算法进行全面评估,并探讨它们在实际应用中的优劣势。
1. 深度优先算法的时间复杂度深度优先算法是一种用于遍历或搜索树或图的算法。
它从图中的某个顶点出发,沿着一条路径一直走到底,直到不能再前进为止,然后回溯到上一个节点,尝试走其他的路径,直到所有路径都被走过为止。
深度优先算法的时间复杂度与图的深度有关。
在最坏情况下,深度优先算法的时间复杂度为O(V+E),其中V表示顶点的数量,E表示边的数量。
2. 广度优先算法的时间复杂度广度优先算法也是一种用于遍历或搜索树或图的算法。
与深度优先算法不同的是,广度优先算法是从图的某个顶点出发,首先访问这个顶点的所有邻接节点,然后再依次访问这些节点的邻接节点,依次类推。
广度优先算法的时间复杂度与图中边的数量有关。
在最坏情况下,广度优先算法的时间复杂度为O(V+E)。
3. 深度优先算法与广度优先算法的比较从时间复杂度的角度来看,深度优先算法和广度优先算法在最坏情况下都是O(V+E),并没有明显的差异。
但从实际运行情况来看,深度优先算法和广度优先算法的性能差异是显而易见的。
在一般情况下,广度优先算法要比深度优先算法快,因为广度优先算法的搜索速度更快,且能够更快地找到最短路径。
4. 个人观点和理解在实际应用中,选择深度优先算法还是广度优先算法取决于具体的问题。
如果要找到两个节点之间的最短路径,那么广度优先算法是更好的选择;而如果要搜索整个图,那么深度优先算法可能是更好的选择。
要根据具体的问题来选择合适的算法。
5. 总结和回顾本文从时间复杂度的角度对深度优先算法和广度优先算法进行了全面评估,探讨了它们的优劣势和实际应用中的选择。
通过对两种算法的时间复杂度进行比较,可以更全面、深刻和灵活地理解深度优先算法和广度优先算法的特点和适用场景。
图的遍历 实验报告

图的遍历实验报告一、引言图是一种非线性的数据结构,由一组节点(顶点)和节点之间的连线(边)组成。
图的遍历是指按照某种规则依次访问图中的每个节点,以便获取或处理节点中的信息。
图的遍历在计算机科学领域中有着广泛的应用,例如在社交网络中寻找关系紧密的人员,或者在地图中搜索最短路径等。
本实验旨在通过实际操作,掌握图的遍历算法。
在本实验中,我们将实现两种常见的图的遍历算法:深度优先搜索(DFS)和广度优先搜索(BFS),并比较它们的差异和适用场景。
二、实验目的1. 理解和掌握图的遍历算法的原理与实现;2. 比较深度优先搜索和广度优先搜索的差异;3. 掌握图的遍历算法在实际问题中的应用。
三、实验步骤实验材料1. 计算机;2. 编程环境(例如Python、Java等);3. 支持图操作的相关库(如NetworkX)。
实验流程1. 初始化图数据结构,创建节点和边;2. 实现深度优先搜索算法;3. 实现广度优先搜索算法;4. 比较两种算法的时间复杂度和空间复杂度;5. 比较两种算法的遍历顺序和适用场景;6. 在一个具体问题中应用图的遍历算法。
四、实验结果1. 深度优先搜索(DFS)深度优先搜索是一种通过探索图的深度来遍历节点的算法。
具体实现时,我们可以使用递归或栈来实现深度优先搜索。
算法的基本思想是从起始节点开始,选择一个相邻节点进行探索,直到达到最深的节点为止,然后返回上一个节点,再继续探索其他未被访问的节点。
2. 广度优先搜索(BFS)广度优先搜索是一种逐层遍历节点的算法。
具体实现时,我们可以使用队列来实现广度优先搜索。
算法的基本思想是从起始节点开始,依次遍历当前节点的所有相邻节点,并将这些相邻节点加入队列中,然后再依次遍历队列中的节点,直到队列为空。
3. 时间复杂度和空间复杂度深度优先搜索和广度优先搜索的时间复杂度和空间复杂度如下表所示:算法时间复杂度空间复杂度深度优先搜索O(V+E) O(V)广度优先搜索O(V+E) O(V)其中,V表示节点的数量,E表示边的数量。
第7章图的深度和广度优先搜索遍历算法

和树的遍历类似,我们希望从图中某顶点出发对图中每个顶点访问一次,而且只访问 一次,这一过程称为图的遍历(traversing graph)。 本节介绍两种遍历图的规则:深度优先搜索和广度优先搜索。 这两种方法既适用于无向图,也适用于有向图。
7.3.1 深度优先搜索遍历 一.思路: 从图中某一点(如A)开始,先访问这一点,然后任选它的一个邻点(如V0) 访问,访问完该点后,再任选这个点V0的一个邻点 ( 如 W )访问,如此向 纵深方向访问。直到某个点没有其他未访问的邻点为止,则返回到前一个点。 再任选它的另一个未访问过的邻点 ( 如X )继续重复上述过程的访问,直到全 部点访问完为止。 图(a)的遍历的结果:V1V2V4V8V5V3V6V7 或V1V3V7V6V2V5V8V4
p
v0 w x v 1
V
0
v 2
V
0
typedef struct {VEXNODE adjlist[MAXLEN]; // 邻接链表表头向量 int vexnum, arcnum; // 顶点数和边数 int kind; // 图的类型 }ADJGRAPH;
W W
X
X
7.3.2 广度优先搜索遍历 一.思路:
V
0
A V
0
W W
XXΒιβλιοθήκη 二.深度优先搜索算法的文字描述: 算法中设一数组visited,表示顶点是否访问过的标志。数组长度为 图的顶点数,初值均置为0,表示顶点均未被访问,当Vi被访问过,即 将visitsd对应分量置为1。将该数组设为全局变量。 { 确定从G中某一顶点V0出发,访问V0; visited[V0] = 1; 找出G中V0的第一个邻接顶点->w; while (w存在) do { if visited[w] == 0 继续进行深度优先搜索; 找出G中V0的下一个邻接顶点->w;} }
图的遍历实验报告

1.问题描述:不少涉及图上操作的算法都是以图的遍历操作为基础的。
试写一个程序,演示在连通的无向图上访问全部结点的操作。
2.基本要求:以邻接表为存储结构,实现连通无向图的深度优先和广度优先遍历。
以用户指定的结点为起点,分别输出每种遍历下的结点访问序列和相应生成树的边集。
3.测试数据:教科书图7.33。
暂时忽略里程,起点为北京。
4.实现提示:设图的结点不超过30个,每一个结点用一个编号表示(如果一个图有n个结点,则它们的编号分别为1,2,…,n)。
通过输入图的全部边输入一个图,每一个边为一个数对,可以对边的输入顺序作出某种限制,注意,生成树的边是有向边,端点顺序不能颠倒。
5.选作内容:(1) .借助于栈类型(自己定义和实现),用非递归算法实现深度优先遍历。
(2) .以邻接表为存储结构,建立深度优先生成树和广度优先生成树,再按凹入表或者树形打印生成树。
1.为实现上述功能,需要有一个图的抽象数据类型。
该抽象数据类型的定义为:ADT Graph{V 是具有相同特性的数据元素的集合,称为顶点集。
R={VR}VR={<v,w> | v ,w v 且P(v,w),<v,w>表示从v 到w 得弧,谓词P(v,w)定义了弧<v,w>的意义或者信息}} ADT Graph2.此抽象数据类型中的一些常量如下:#define TRUE 1#define FALSE 0#define OK 1#define max_n 20 //最大顶点数typedef char VertexType[20];typedef enum{DG, DN, AG, AN} GraphKind;enum BOOL{False,True};3.树的结构体类型如下所示:typedef struct{ //弧结点与矩阵的类型int adj; //VRType为弧的类型。
图--0,1;网--权值int *Info; //与弧相关的信息的指针,可省略}ArcCell, AdjMatrix[max_n][max_n];typedef struct{VertexType vexs[max_n]; //顶点AdjMatrix arcs; //邻接矩阵int vexnum, arcnum; //顶点数,边数}MGraph;//队列的类型定义typedef int QElemType;typedef struct QNode{QElemType data;struct QNode *next;}QNode, *QueuePtr;typedef struct{QueuePtr front;QueuePtr rear;}LinkQueue;4.本程序包含三个模块1).主程序模块void main( ){创建树;深度优先搜索遍历;广度优先搜索遍历;}2).树模块——实现树的抽象数据类型3).遍历模块——实现树的深度优先遍历和广度优先遍历各模块之间的调用关系如下:主程序模块树模块遍历模块#include "stdafx.h"#include<iostream>using namespace std;#define TRUE 1#define FALSE 0#define OK 1#define max_n 20 //最大顶点数typedef char VertexType[20];typedef enum{DG, DN, AG, AN} GraphKind;enum BOOL{False,True};typedef struct{ //弧结点与矩阵的类型int adj; //VRType为弧的类型。
数据结构课设——有向图的深度、广度优先遍历及拓扑排序

数据结构课设——有向图的深度、⼴度优先遍历及拓扑排序任务:给定⼀个有向图,实现图的深度优先, ⼴度优先遍历算法,拓扑有序序列,并输出相关结果。
功能要求:输⼊图的基本信息,并建⽴图存储结构(有相应提⽰),输出遍历序列,然后进⾏拓扑排序,并测试该图是否为有向⽆环图,并输出拓扑序列。
按照惯例,先上代码,注释超详细:#include<stdio.h>#include<stdlib.h>#include<malloc.h>#pragma warning(disable:4996)#define Max 20//定义数组元素最⼤个数(顶点最⼤个数)typedef struct node//边表结点{int adjvex;//该边所指向结点对应的下标struct node* next;//该边所指向下⼀个结点的指针}eNode;typedef struct headnode//顶点表结点{int in;//顶点⼊度char vertex;//顶点数据eNode* firstedge;//指向第⼀条边的指针,边表头指针}hNode;typedef struct//邻接表(图){hNode adjlist[Max];//以数组的形式存储int n, e;//顶点数,边数}linkG;//以邻接表的存储结构创建图linkG* creat(linkG* g){int i, k;eNode* s;//边表结点int n1, e1;char ch;g = (linkG*)malloc(sizeof(linkG));//申请结点空间printf("请输⼊顶点数和边数:");scanf("%d%d", &n1, &e1);g->n = n1;g->e = e1;printf("顶点数:%d 边数:%d\n", g->n, g->e);printf("请输⼊顶点信息(字母):");getchar();//因为接下来要输⼊字符串,所以getchar⽤于承接上⼀条命令的结束符for (i = 0; i < n1; i++){scanf("%c", &ch);g->adjlist[i].vertex = ch;//获得该顶点数据g->adjlist[i].firstedge = NULL;//第⼀条边设为空}printf("\n打印顶点下标及顶点数据:\n");for (i = 0; i < g->n; i++)//循环打印顶点下标及顶点数据{printf("顶点下标:%d 顶点数据:%c\n", i, g->adjlist[i].vertex);}getchar();int i1, j1;//相连接的两个顶点序号for (k = 0; k < e1; k++)//建⽴边表{printf("请输⼊对<i,j>(空格分隔):");scanf("%d%d", &i1, &j1);s = (eNode*)malloc(sizeof(eNode));//申请边结点空间s->adjvex = j1;//边所指向结点的位置,下标为j1s->next = g->adjlist[i1].firstedge;//将当前s的指针指向当前顶点上指向的结点g->adjlist[i1].firstedge = s;//将当前顶点的指针指向s}return g;//返回指针g}int visited[Max];//标记是否访问void DFS(linkG* g, int i)//深度优先遍历{eNode* p;printf("%c ", g->adjlist[i].vertex);visited[i] = 1;//将已访问过的顶点visited值改为1p = g->adjlist[i].firstedge;//p指向顶点i的第⼀条边while (p)//p不为NULL时(边存在){if (visited[p->adjvex] != 1)//如果没有被访问DFS(g, p->adjvex);//递归}p = p->next;//p指向下⼀个结点}}void DFSTravel(linkG* g)//遍历⾮连通图{int i;printf("深度优先遍历;\n");//printf("%d\n",g->n);for (i = 0; i < g->n; i++)//初始化为0{visited[i] = 0;}for (i = 0; i < g->n; i++)//对每个顶点做循环{if (!visited[i])//如果没有被访问{DFS(g, i);//调⽤DFS函数}}}void BFS(linkG* g, int i)//⼴度优先遍历{int j;eNode* p;int q[Max], front = 0, rear = 0;//建⽴顺序队列⽤来存储,并初始化printf("%c ", g->adjlist[i].vertex);visited[i] = 1;//将已经访问过的改成1rear = (rear + 1) % Max;//普通顺序队列的话,这⾥是rear++q[rear] = i;//当前顶点(下标)队尾进队while (front != rear)//队列⾮空{front = (front + 1) % Max;//循环队列,顶点出队j = q[front];p = g->adjlist[j].firstedge;//p指向出队顶点j的第⼀条边while (p != NULL){if (visited[p->adjvex] == 0)//如果未被访问{printf("%c ", g->adjlist[p->adjvex].vertex);visited[p->adjvex] = 1;//将该顶点标记数组值改为1rear = (rear + 1) % Max;//循环队列q[rear] = p->adjvex;//该顶点进队}p = p->next;//指向下⼀个结点}}}void BFSTravel(linkG* g)//遍历⾮连通图{int i;printf("⼴度优先遍历:\n");for (i = 0; i < g->n; i++)//初始化为0{visited[i] = 0;}for (i = 0; i < g->n; i++)//对每个顶点做循环{if (!visited[i])//如果没有被访问过{BFS(g, i);//调⽤BFS函数}}}//因为拓扑排序要求⼊度为0,所以需要先求出每个顶点的⼊度void inDegree(linkG* g)//求图顶点⼊度{eNode* p;int i;for (i = 0; i < g->n; i++)//循环将顶点⼊度初始化为0{g->adjlist[i].in = 0;}for (i = 0; i < g->n; i++)//循环每个顶点{p = g->adjlist[i].firstedge;//获取第i个链表第1个边结点指针while (p != NULL)///当p不为空(边存在){g->adjlist[p->adjvex].in++;//该边终点结点⼊度+1p = p->next;//p指向下⼀个边结点}printf("顶点%c的⼊度为:%d\n", g->adjlist[i].vertex, g->adjlist[i].in);}void topo_sort(linkG *g)//拓扑排序{eNode* p;int i, k, gettop;int top = 0;//⽤于栈指针的下标索引int count = 0;//⽤于统计输出顶点的个数int* stack=(int *)malloc(g->n*sizeof(int));//⽤于存储⼊度为0的顶点for (i=0;i<g->n;i++)//第⼀次搜索⼊度为0的顶点{if (g->adjlist[i].in==0){stack[++top] = i;//将⼊度为0的顶点进栈}}while (top!=0)//当栈不为空时{gettop = stack[top--];//出栈,并保存栈顶元素(下标)printf("%c ",g->adjlist[gettop].vertex);count++;//统计顶点//接下来是将邻接点的⼊度减⼀,并判断该点⼊度是否为0p = g->adjlist[gettop].firstedge;//p指向该顶点的第⼀条边的指针while (p)//当p不为空时{k = p->adjvex;//相连接的顶点(下标)g->adjlist[k].in--;//该顶点⼊度减⼀if (g->adjlist[k].in==0){stack[++top] = k;//如果⼊度为0,则进栈}p = p->next;//指向下⼀条边}}if (count<g->n)//如果输出的顶点数少于总顶点数,则表⽰有环{printf("\n有回路!\n");}free(stack);//释放空间}void menu()//菜单{system("cls");//清屏函数printf("************************************************\n");printf("* 1.建⽴图 *\n");printf("* 2.深度优先遍历 *\n");printf("* 3.⼴度优先遍历 *\n");printf("* 4.求出顶点⼊度 *\n");printf("* 5.拓扑排序 *\n");printf("* 6.退出 *\n");printf("************************************************\n");}int main(){linkG* g = NULL;int c;while (1){menu();printf("请选择:");scanf("%d", &c);switch (c){case1:g = creat(g); system("pause");break;case2:DFSTravel(g); system("pause");break;case3:BFSTravel(g); system("pause");break;case4:inDegree(g); system("pause");break;case5:topo_sort(g); system("pause");break;case6:exit(0);break;}}return0;}实验⽤图:运⾏结果:关于深度优先遍历 a.从图中某个顶点v 出发,访问v 。
深度优先和广度优先比较
深度优先和⼴度优先⽐较区别:1)⼆叉树的深度优先遍历的⾮递归的通⽤做法是采⽤栈,⼴度优先遍历的⾮递归的通⽤做法是采⽤队列。
2)深度优先遍历:对每⼀个可能的分⽀路径深⼊到不能再深⼊为⽌,⽽且每个结点只能访问⼀次。
要特别注意的是,⼆叉树的深度优先遍历⽐较特殊,可以细分为先序遍历、中序遍历、后序遍历。
具体说明如下:先序遍历:对任⼀⼦树,先访问根,然后遍历其左⼦树,最后遍历其右⼦树。
中序遍历:对任⼀⼦树,先遍历其左⼦树,然后访问根,最后遍历其右⼦树。
后序遍历:对任⼀⼦树,先遍历其左⼦树,然后遍历其右⼦树,最后访问根。
⼴度优先遍历:⼜叫层次遍历,从上往下对每⼀层依次访问,在每⼀层中,从左往右(也可以从右往左)访问结点,访问完⼀层就进⼊下⼀层,直到没有结点可以访问为⽌。
3)深度优先搜素算法:不全部保留结点,占⽤空间少;有回溯操作(即有⼊栈、出栈操作),运⾏速度慢。
⼴度优先搜索算法:保留全部结点,占⽤空间⼤;⽆回溯操作(即⽆⼊栈、出栈操作),运⾏速度快。
通常深度优先搜索法不全部保留结点,扩展完的结点从数据库中弹出删去,这样,⼀般在数据库中存储的结点数就是深度值,因此它占⽤空间较少。
所以,当搜索树的结点较多,⽤其它⽅法易产⽣内存溢出时,深度优先搜索不失为⼀种有效的求解⽅法。
⼴度优先搜索算法,⼀般需存储产⽣的所有结点,占⽤的存储空间要⽐深度优先搜索⼤得多,因此,程序设计中,必须考虑溢出和节省内存空间的问题。
但⼴度优先搜索法⼀般⽆回溯操作,即⼊栈和出栈的操作,所以运⾏速度⽐深度优先搜索要快些深度优先:前序遍历:35,20,15,16,29,28,30,40,50,45,55中序遍历:15,16,20,28,29,30,35,40,45,50,55后序遍历:16,15,28,30,29,20,45,55,50,40,35⼴度优先遍历:35 20 40 15 29 50 16 28 30 45 55代码:package www.hhy;import java.beans.beancontext.BeanContextChild;import java.util.*;class Binarytree {class TreeNode{int value;TreeNode left;TreeNode right;public TreeNode(int value) {this.value = value;}}//⽤递归创建⼆叉树public int i = 0;TreeNode creatTesttree(String s){TreeNode root = null;if (s.charAt(i)!='#') {root = new TreeNode(s.charAt(i));i++;root.left = creatTesttree(s);root.right = creatTesttree(s);}else{i++;}return root;}//⼆叉树的前序遍历递归void binaryTreePrevOrder(TreeNode root){if(root==null){return;}System.out.println(root.value+" ");binaryTreePrevOrder(root.left);binaryTreePrevOrder(root.right);}//⼆叉树的中序遍历递归void binaryTreeInOrder(TreeNode root){if(root==null){return;}binaryTreeInOrder(root.left);System.out.println(root.value+" ");binaryTreeInOrder(root.right);}//⼆叉树的后续遍历递归void binaryTreePostOrder(TreeNode root){if(root==null){return;}binaryTreePostOrder(root.left);binaryTreePostOrder(root.right);System.out.println(root.value+" ");}//层序遍历void binaryTreeLevelOrder(TreeNode root,int level){if(root ==null||level<1){return;}if(level==1){System.out.print(root.value+" ");}binaryTreeLevelOrder(root.left,level-1);binaryTreeLevelOrder(root.right,level-1);}void BTreeLevelOrder(TreeNode root){if (root == null)return;int dep = getHeight(root);for (int i = 1; i <= dep; i++){binaryTreeLevelOrder(root,i);}}//⼆叉树的层序遍历⾮递归void binaryTreeLevelOrder(TreeNode root) {Queue<TreeNode> queue = new LinkedList<>();if(root != null) {queue.offer(root);//LinkedList offer add}while (!queue.isEmpty()) {//1、拿到队头的元素把队头元素的左右⼦树⼊队 TreeNode cur = queue.poll();System.out.print(cur.value+" ");//2、不为空的时候才能⼊队if(cur.left != null) {queue.offer(cur.left);}if(cur.right != null) {queue.offer(cur.right);}}}//⼆叉树的前序遍历⾮递归void binaryTreePrevOrderNonR(TreeNode root){Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;TreeNode top = null;while (cur != null || !stack.empty()) {while (cur != null) {stack.push(cur);System.out.print(cur.value + " ");cur = cur.left;}top = stack.pop();cur = top.right;}System.out.println();}//⼆叉树的中序遍历⾮递归void binaryTreeInOrderNonR(TreeNode root){Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;TreeNode top = null;while (cur != null || !stack.empty()) {while (cur != null) {stack.push(cur);cur = cur.left;}top = stack.pop();System.out.print(top.value+" ");cur = top.right;}System.out.println();}//⼆叉树的后序遍历⾮递归void binaryTreePostOrderNonR(TreeNode root) {Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;TreeNode prev = null;while (cur != null || !stack.empty()) {while (cur != null) {stack.push(cur);cur = cur.left;}cur = stack.peek();//L D//cur.right == prev 代表的是 cur的右边已经打印过了if(cur.right == null || cur.right == prev) {stack.pop();System.out.println(cur.value);prev = cur;cur = null;}else {cur = cur.right;}}}//⼆叉树的节点个数递归int getSize(TreeNode root){if(root==null){return 0;}return getSize(root.left)+getSize(root.right)+1;}//⼆叉树的叶⼦节点的个数递归int getLeafSize(TreeNode root){if(root==null){return 0;}if(root.left==null && root.right==null){return 1;}return getLeafSize(root.left)+getLeafSize(root.right); }//⼆叉树得到第K层结点的个数int getKlevelSize(TreeNode root ,int k){if(root==null){return 0;}if(k == 1){return 1;}return getKlevelSize(root.left,k-1)+getKlevelSize(root.right,k-1);}//⼆叉树查找并返回该结点递归// 查找,依次在⼆叉树的根、左⼦树、// 右⼦树中查找 value,如果找到,返回结点,否则返回 nullTreeNode find(TreeNode root, int value){if(root == null) {return null;}if(root.value == value){return root;}TreeNode ret = find(root.left,value);if(ret != null) {return ret;}ret = find(root.right,value);if(ret != null) {return ret;}return null;}//⼆叉树的⾼度int getHeight(TreeNode root){if(root==null){return 0;}int leftHeight = getHeight(root.left);int rightHeight = getHeight(root.right);return leftHeight>rightHeight ? leftHeight+1:rightHeight+1;}//判断⼀个树是不是完全⼆叉树public int binaryTreeComplete(TreeNode root) {Queue<TreeNode> queue = new LinkedList<TreeNode>();if(root != null) {queue.add(root);//offer}while(!queue.isEmpty()) {TreeNode cur = queue.peek();queue.poll();if(cur != null) {queue.add(cur.left);queue.add(cur.right);}else {break;}}while(!queue.isEmpty()) {TreeNode cur = queue.peek();if (cur != null){//说明不是满⼆叉树return -1;}else{queue.poll();}}return 0;//代表是完全⼆叉树}//检查两棵树是否是相同的,如果两棵树结构相同,并且在结点上的值相同,那么这两棵树是相同返回true public boolean isSameTree(TreeNode p,TreeNode q){if((p==null&&q!=null)||(p!=null&&q==null)){}if(p==null && q==null){return true;}if(p.value!=q.value){return false;}return isSameTree(p.left,q.left)&&isSameTree(p.right,q.left);}//检查是否为⼦树public boolean isSubTree(TreeNode s,TreeNode t){if(s==null||t==null){return false;}if(isSameTree(s,t)){return true;}else if (isSubTree(s.left,t)){return true;}else if(isSubTree(s.right,t)){return true;}else{return false;}}//1.判断是否为平衡⼆叉树,左右⼦树的⾼度之差不超过 "1"(⼤根本⾝是平衡⼆叉树,左右⼦树也必须是平衡⼆叉树) // 时间复杂度为n^2//2.求复杂度为O(n)的解法public boolean isBanlanced(TreeNode root){if(root==null){return true;}else{int leftHeight = getHeight(root.left);int rightHeight = getHeight(root.right);return Math.abs(leftHeight-rightHeight)<2&&isBanlanced(root.left)&&isBanlanced(root.right);}}//判断是否为对称⼆叉树public boolean isSymmetric(TreeNode root){if(root==null){return true;}return isSymmetric(root.left,root.right);}public boolean isSymmetric(TreeNode lefttree,TreeNode righttree){if((lefttree==null && righttree!=null)||(lefttree!=null && righttree ==null)){return false;}if(lefttree == null && righttree == null){return true;}return lefttree.value == righttree.value && isSymmetric(lefttree.left,righttree.right)&& isSymmetric(lefttree.right,righttree.left);}//⼆叉树创建字符串⾮递归写法public String tree2str(TreeNode t){StringBuilder sb = new StringBuilder();tree2strchild(t,sb);return sb.toString();}public void tree2strchild(TreeNode t ,StringBuilder sb){if (t==null){}sb.append(t.value);if (t.left!=null){sb.append("(");tree2strchild(t.left,sb);sb.append(")");}else {if (t.right==null){}}}//⼆叉树字符串递归写法public String CreateStr(TreeNode t){if(t==null){return "";}if(t.left==null&&t.right==null){return t.value+"";}if(t.left==null){return t.value+"()"+"("+CreateStr(t.right)+")";}if(t.right==null){return t.value+"("+CreateStr(t.left)+")";}return t.value+"("+CreateStr(t.left)+")"+"("+CreateStr(t.right)+")";}public int rob(TreeNode root) {if (root == null) return 0;return Math.max(robOK(root), robNG(root));}private int robOK(TreeNode root) {if (root == null) return 0;return root.value + robNG(root.left) + robNG(root.right);}private int robNG(TreeNode root) {if (root == null) return 0;return rob(root.left) + rob(root.right);}//⼆叉树的公共祖先public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if(root==null){return null;}if(root==p||root==q){return root;}TreeNode leftTree = lowestCommonAncestor(root.left,p,q);//p||q nullTreeNode rightTree = lowestCommonAncestor(root.right,p,q);//p||q null//3if(leftTree!=null && rightTree!=null){return root;}//左边找到else if (leftTree!=null ){return leftTree;}//右边找到else if(rightTree!=null){return rightTree;}//都没找到的情况下return null;}//⼆叉搜索树,若他的左⼦树不为空,左⼦树上的所有节点都⼩于根节点,//如果他的右⼦树不为空,右⼦树上的所有节点都⼤于根节点//最终他的中序排列都是有序结果//输⼊⼀棵⼆叉搜索树,将该⼆叉搜索树转换成⼀个排序的双向链表。
数据结构的实验报告
一、实验目的本次实验旨在让学生掌握数据结构的基本概念、逻辑结构、存储结构以及各种基本操作,并通过实际编程操作,加深对数据结构理论知识的理解,提高编程能力和算法设计能力。
二、实验内容1. 线性表(1)顺序表1)初始化顺序表2)向顺序表插入元素3)从顺序表删除元素4)查找顺序表中的元素5)顺序表的逆序操作(2)链表1)创建链表2)在链表中插入元素3)在链表中删除元素4)查找链表中的元素5)链表的逆序操作2. 栈与队列(1)栈1)栈的初始化2)入栈操作3)出栈操作4)获取栈顶元素5)判断栈是否为空(2)队列1)队列的初始化2)入队操作3)出队操作4)获取队首元素5)判断队列是否为空3. 树与图(1)二叉树1)创建二叉树2)遍历二叉树(前序、中序、后序)3)求二叉树的深度4)求二叉树的宽度5)二叉树的镜像(2)图1)创建图2)图的深度优先遍历3)图的广度优先遍历4)最小生成树5)最短路径三、实验过程1. 线性表(1)顺序表1)初始化顺序表:创建一个长度为10的顺序表,初始化为空。
2)向顺序表插入元素:在顺序表的第i个位置插入元素x。
3)从顺序表删除元素:从顺序表中删除第i个位置的元素。
4)查找顺序表中的元素:在顺序表中查找元素x。
5)顺序表的逆序操作:将顺序表中的元素逆序排列。
(2)链表1)创建链表:创建一个带头结点的循环链表。
2)在链表中插入元素:在链表的第i个位置插入元素x。
3)在链表中删除元素:从链表中删除第i个位置的元素。
4)查找链表中的元素:在链表中查找元素x。
5)链表的逆序操作:将链表中的元素逆序排列。
2. 栈与队列(1)栈1)栈的初始化:创建一个栈,初始化为空。
2)入栈操作:将元素x压入栈中。
3)出栈操作:从栈中弹出元素。
4)获取栈顶元素:获取栈顶元素。
5)判断栈是否为空:判断栈是否为空。
(2)队列1)队列的初始化:创建一个队列,初始化为空。
2)入队操作:将元素x入队。
3)出队操作:从队列中出队元素。
算法设计:深度优先遍历和广度优先遍历
算法设计:深度优先遍历和广度优先遍历实现深度优先遍历过程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出发的未检测过的边(x,y)。
若发现顶点y已访问过,则重新选择另一条从x出发的未检测过的边,否则沿边(x,y)到达未曾访问过的y,对y访问并将其标记为已访问过;然后从y开始搜索,直到搜索完从y出发的所有路径,即访问完所有从y出发可达的顶点之后,才回溯到顶点x,并且再选择一条从x出发的未检测过的边。
浅析深度优先和广度优先遍历实现过程、区别及使用场景
浅析深度优先和⼴度优先遍历实现过程、区别及使⽤场景⼀、什么是深度/⼴度优先遍历? 深度优先遍历简称DFS(Depth First Search),⼴度优先遍历简称BFS(Breadth First Search),它们是遍历图当中所有顶点的两种⽅式。
这两种遍历⽅式有什么不同呢?我们来举个栗⼦: 我们来到⼀个游乐场,游乐场⾥有11个景点。
我们从景点0开始,要玩遍游乐场的所有景点,可以有什么样的游玩次序呢?1、深度优先遍历 第⼀种是⼀头扎到底的玩法。
我们选择⼀条⽀路,尽可能不断地深⼊,如果遇到死路就往回退,回退过程中如果遇到没探索过的⽀路,就进⼊该⽀路继续深⼊。
在图中,我们⾸先选择景点1的这条路,继续深⼊到景点7、景点8,终于发现⾛不动了: 于是,我们退回到景点7,然后探索景点10,⼜⾛到了死胡同。
于是,退回到景点1,探索景点9: 按照这个思路,我们再退回到景点0,后续依次探索景点2、3、5、4、发现相邻的都玩过了,再回退到3,再接着玩6,终于玩遍了整个游乐场: 具体次序如下图,景点旁边的数字代表探索次序。
当然还可以有别的排法。
像这样先深⼊探索,⾛到头再回退寻找其他出路的遍历⽅式,就叫做深度优先遍历(DFS)。
这⽅式看起来很像⼆叉树的前序遍历。
没错,其实⼆叉树的前序、中序、后序遍历,本质上也可以认为是深度优先遍历。
2、⼴度优先遍历 除了像深度优先遍历这样⼀头扎到底的玩法以外,我们还有另⼀种玩法:⾸先把起点相邻的⼏个景点玩遍,然后去玩距离起点稍远⼀些(隔⼀层)的景点,然后再去玩距离起点更远⼀些(隔两层)的景点… 在图中,我们⾸先探索景点0的相邻景点1、2、3、4: 接着,我们探索与景点0相隔⼀层的景点7、9、5、6: 最后,我们探索与景点0相隔两层的景点8、10: 像这样⼀层⼀层由内⽽外的遍历⽅式,就叫做⼴度优先遍历(BFS)。
这⽅式看起来很像⼆叉树的层序遍历。
没错,其实⼆叉树的层序遍历,本质上也可以认为是⼴度优先遍历。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验报告学院(系)名称:计算机与通信工程学院
一、实验目的
理解图的逻辑特点;掌握理解图的两种主要存储结构(邻接矩阵和邻接表),掌握图的构造、深度优先遍历、广度优先遍历算法
二、实验题目与要求
1. 每位同学按下述要现相应算法:根据从键盘输入的数据创建图(图的存储结构可采用
邻接矩阵或邻接表),并对图进行深度优先搜索和广度优先搜索
1)问题描述:在主程序中提供下列菜单:
1…图的建立
2…深度优先遍历图
3…广度优先遍历图
0…结束
2)实验要求:图的存储可采用邻接表或邻接矩阵;定义下列过程:
CreateGraph(): 按从键盘的数据建立图
DFSGrahp():深度优先遍历图
BFSGrahp():广度优先遍历图
3)实验提示:
图的存储可采用邻接表或邻接矩阵;
图存储数据类型定义(邻接表存储)
# define MAX_VERTEX_NUM 8 //顶点最大个数
typedef struct ArcNode
{ int adjvex;
struct ArcNode *nextarc;
int weight; //边的权
}ArcNode; //表结点
# define VertexType int //顶点元素类型
typedef struct VNode
{ int degree,indegree; //顶点的度,入度
VertexType data;
ArcNode *firstarc; }Vnode /*头结点*/;
typedef struct{
Vnode vertices[MAX_VERTEX_NUM];
int vexnum,arcnum;//顶点的实际数,边的实际数
}ALGraph;
4)注意问题:
注意理解各算法实现时所采用的存储结构。
注意区别正、逆邻接。
2. 拓扑排序:给出一个图的结构,输出其拓扑排序序列(顶点序列用空格隔开),要求在同等条
件下,编号小的顶点在前。
3.利用最小生成树算法解决通信网的总造价最低问题
1)问题描述:若在n 个城市之间建通信网络,架设n-1 条线路即可。
如何以最低的经济代价建设这个通信网,是一个网络的最小生成树问题。
2)实验要求:利用Prim 算法求网的最小生成树。
3) 实现提示:通信线路一旦建立,必然是双向的。
因此,构造最小生成树的网一定是无向
网。
为简单起见,图的顶点数不超过10 个,网中边的权值设置成小于100。
三、实验过程与实验结果
应包括如下主要容:
➢数据结构定义
➢图是由定点集合及定点间的关系集合组成的一种数据结构,其形式化定义为Graph = (V,E)其中,V = {x|x∈某个数据对象}是定点的有限非空集合;E = {(x,y)|x,y∈V∧Path(x,y)}
是顶点之间关系的有限集合,叫做便集。
集合E中的Path(x,y)表示顶点x和顶点y之间
有一条直接连线,即(x,y)表示一条边,它是有方向的。
➢算法设计思路简介
➢
➢算法描述:可以用自然语言、伪代码或流程图等方式
➢1、
➢图的深度优先搜索:
➢
➢
➢
➢2、➢
➢
➢
➢
➢
➢3、➢
➢
➢
➢
➢
➢算法时间复杂度分析
➢1、
➢深度优先遍历:O(n*n). ➢广度优先遍历:O(n*n). ➢2、
➢O(n+e).
➢3、。