数据结构(17)--图的遍历DFS和BFS
2016-03-04 00:20
447 查看
参考书籍:数据结构(C语言版)严蔚敏吴伟民编著清华大学出版社
从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次。这一过程就叫做图的遍历。
示例:
从图中某顶点V0出发,访问此顶点,然后依次从V0的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和V0有路径相通的顶点都被访问到;
若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点;
重复上述过程,直至图中所有顶点都被访问到为止。
分析:
在遍历图时,对图中每个顶点至多调用一次DFS函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。
因此,遍历图的过程实质上是对每个顶点查找其邻接点的过程。其耗费的时间则取决于所采用的存储结构。 当使用二维数组表示邻接矩阵作图的存储结构时,查找每个顶点的邻接点所需时间为O(n^2),其中n为顶点数。而当以邻接表作图的存储结构时,找邻接点所需时间为O(e),其中e为无向图中边的数目或有向图中弧的数目。由此,当以邻接表作存储结构时,深度优先搜遍遍历图的时间复杂度为O(n+e)。
代码实现:
从图中某个顶点V0出发,并在访问此顶点后依次访问V0的所有未被访问过的邻接点,之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和V0有路径相通的顶点都被访问到;
若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点;
重复上述过程,直至图中所有顶点都被访问到为止。
分析:
每个顶点至多进一次队列。遍历图的过程实质上是通过边或弧找邻接点的过程,因此广度优先搜索遍历图的时间复杂度和深度优先搜索遍历相同,两者不同之处仅仅在于对顶点访问的顺序不同。
代码实现:
从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次。这一过程就叫做图的遍历。
示例:
1.深度优先遍历
基本思想:从图中某顶点V0出发,访问此顶点,然后依次从V0的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和V0有路径相通的顶点都被访问到;
若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点;
重复上述过程,直至图中所有顶点都被访问到为止。
分析:
在遍历图时,对图中每个顶点至多调用一次DFS函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。
因此,遍历图的过程实质上是对每个顶点查找其邻接点的过程。其耗费的时间则取决于所采用的存储结构。 当使用二维数组表示邻接矩阵作图的存储结构时,查找每个顶点的邻接点所需时间为O(n^2),其中n为顶点数。而当以邻接表作图的存储结构时,找邻接点所需时间为O(e),其中e为无向图中边的数目或有向图中弧的数目。由此,当以邻接表作存储结构时,深度优先搜遍遍历图的时间复杂度为O(n+e)。
代码实现:
int visited[MAX_VERTEX_NUM];//访问标识数组
void DFS(ALGraph G, int ivex){ //从第i个顶点出发递归的深度优先遍历图G visited[ivex] = 1; printf("v%d ", G.vexs[ivex].data);//打印(访问)该顶点 for(ArcNode *p = G.vexs[ivex].firstarc; p; p = p->nextarc){//对于第ivex个顶点的每个未被访问的邻接点递归调用DFS if(!visited[p->adjvex]){ DFS(G, p->adjvex); } } }
//深度优先遍历无向图G(相当于树的先序遍历)(递归算法) void DFSTraverseGraph(ALGraph G){ //初始化访问标志数组 for(int i = 0; i < G.vexnum; i++){ visited[i] = 0;//0表示未被访问,1表示已被访问 } printf("请输入遍历的起始顶点(如:v1):"); VertexType startVex; scanf("v%d", &startVex); int startVexPos = locateVex(G, startVex); printf("一条深度优先遍历序列为:"); if(!visited[startVexPos]) DFS(G, startVexPos); printf("\n"); /* for(i = 0; i < G.vexnum; i++){//图中每个顶点至多调用一次DFS函数 if(!visited[i]){//对还未访问过的顶点调用DFS DFS(G, i); } } */ }
//深度优先遍历无向图G(相当于树的先序遍历)(非递归算法) void DFSTraverseGraph2(ALGraph G){ int stack[MAX_VERTEX_NUM];//维护一个栈来存储访问图的顶点的(位置)信息 int top = 0;//初始化栈顶指针,为空栈 //初始化访问标志数组 for(int i = 0; i < G.vexnum; i++){ visited[i] = 0;//0表示未被访问,1表示已被访问 } printf("请输入遍历的起始顶点(如:v1):"); VertexType startVex; scanf("v%d", &startVex); int startVexPos = locateVex(G, startVex); printf("一条深度优先遍历序列为:"); ArcNode *p;// = G.vexs[startVexPos].firstarc; int ivex = startVexPos; while(!visited[ivex] || top!=-1){//栈不为空 if(!visited[ivex]){//第vex结点没有被访问过 visited[ivex] = 1; printf("v%d ", G.vexs[ivex].data); stack[top++] = ivex; } p = G.vexs[ivex].firstarc; while(p && visited[p->adjvex])//p不为空且p已经被访问过,就跳过 p = p->nextarc; //此时p指向以当前顶点为头的且未被访问第一个尾顶点 if(p){//如果p不为空 ivex = p->adjvex; }else{//如果p为空,说明当前顶点的所有和他有路径相通的顶点均已访问,则栈顶元素出栈,查找下一个尚未被访问的顶点 ivex = stack[--top];//栈顶元素出栈 } } printf("\n"); }
2.广度优先遍历
基本思想:从图中某个顶点V0出发,并在访问此顶点后依次访问V0的所有未被访问过的邻接点,之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和V0有路径相通的顶点都被访问到;
若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点;
重复上述过程,直至图中所有顶点都被访问到为止。
分析:
每个顶点至多进一次队列。遍历图的过程实质上是通过边或弧找邻接点的过程,因此广度优先搜索遍历图的时间复杂度和深度优先搜索遍历相同,两者不同之处仅仅在于对顶点访问的顺序不同。
代码实现:
int visited[MAX_VERTEX_NUM];//访问标识数组
//广度优先遍历无向图G(相当于树的按层次遍历)(非递归算法) void BFSTraverseGraph(ALGraph G){ int queue[MAX_VERTEX_NUM];//维护一个队列来存储访问图的顶点的(位置)信息 int front = 0, rail = 0;//初始化队头、队尾指针,为空队列 //初始化访问标志数组 for(int i = 0; i < G.vexnum; i++){ visited[i] = 0;//0表示未被访问,1表示已被访问 } printf("请输入遍历的起始顶点(如:v1):"); VertexType startVex; scanf("v%d", &startVex); int startVexPos = locateVex(G, startVex); printf("一条广度优先遍历序列为:"); queue[rail++] = startVexPos;//起点先入队 int ivex;// = startVexPos; ArcNode *p; while(front != rail){//不是空队列 ivex = queue[front++];//队头元素出队 if(!visited[ivex]){ visited[ivex] = 1; printf("v%d ", G.vexs[ivex].data); } p = G.vexs[ivex].firstarc; while(p){//p指向与ivex的邻接的(同一个层次的)还未被顶点 if(!visited[p->adjvex]) queue[rail++] = p->adjvex;//入队 p = p->nextarc; } } printf("\n"); }
3.演示
//以无向图的邻接表作为存储结构,实现图深度优先遍历算法 #include<stdio.h> #include<stdlib.h> /* 图的表示方法 DG(有向图)或者DN(有向网):邻接矩阵、邻接表(逆邻接表--为求入度)、十字链表 UDG(无向图)或者UDN(无向网):邻接矩阵、邻接表、邻接多重表 */ #define MAX_VERTEX_NUM 10//最大顶点数目 #define NULL 0 //typedef int VRType;//对于带权图或网,则为相应权值 typedef int VertexType;//顶点类型 //typedef enum GraphKind {DG, DN, UDG, UDN}; //有向图:0,有向网:1,无向图:2,无向 typedef struct ArcNode{ int adjvex;//该弧所指向的顶点的在图中位置 //VRType w;//弧的相应权值 struct ArcNode *nextarc;//指向下一条边的指针 }ArcNode;//弧结点信息 typedef struct VNode{ VertexType data;//顶点信息 ArcNode *firstarc;//指向第一条依附该顶点的弧的指针 }VNode, AdjVexList[MAX_VERTEX_NUM];//顶点结点信息 typedef struct{ AdjVexList vexs;//顶点向量 int vexnum, arcnum;//图的当前顶点数和弧数 //GraphKind kind;//图的种类标志 }ALGraph;//邻接表表示的图 //若图G中存在顶点v,则返回v在图中的位置信息,否则返回其他信息 int locateVex(ALGraph G, VertexType v){ for(int i = 0; i < G.vexnum; i++){ if(G.vexs[i].data == v) return i; } return -1;//图中没有该顶点 } //采用邻接表表示法构造无向图G void createUDN(ALGraph &G){ printf("输入顶点数和弧数如:(5,3):"); scanf("%d,%d", &G.vexnum, &G.arcnum); //构造顶点向量,并初始化 printf("输入%d个顶点(以空格隔开如:v1 v2 v3):", G.vexnum); getchar();//吃掉换行符 for(int m = 0; m < G.vexnum; m++){ scanf("v%d", &G.vexs[m].data); G.vexs[m].firstarc = NULL;//初始化为空指针////////////////重要!!! getchar();//吃掉空格符 } //构造邻接表 VertexType v1, v2;//分别是一条弧的弧尾和弧头(起点和终点) //VRType w;//对于无权图或网,用0或1表示相邻否;对于带权图或网,则为相应权值 printf("\n每行输入一条弧依附的顶点(先弧尾后弧头)(如:v1v2):\n"); fflush(stdin);//清除残余后,后面再读入时不会出错 int i = 0, j = 0; for(int k = 0; k < G.arcnum; k++){ scanf("v%dv%d",&v1, &v2); fflush(stdin);//清除残余后,后面再读入时不会出错 i = locateVex(G, v1);//弧起点 j = locateVex(G, v2);//弧终点 //采用“头插法”在各个顶点的弧链头部插入弧结点 ArcNode *p1 = (ArcNode *)malloc(sizeof(ArcNode));//构造一个弧结点,作为弧vivj的弧头(终点) p1->adjvex = j; //p1->w = w; p1->nextarc = G.vexs[i].firstarc; G.vexs[i].firstarc = p1; ArcNode *p2 = (ArcNode *)malloc(sizeof(ArcNode));//构造一个弧结点,作为弧vivj的弧尾(起点) p2->adjvex = i; //p2->w = w; p2->nextarc = G.vexs[j].firstarc; G.vexs[j].firstarc = p2; } }
/*测试:8,9 v1 v2 v3 v4 v5 v6 v7 v8 v9 v1v2 v1v3 v2v4 v2v5 v3v6 v3v7 v4v8 v5v8 v6v7 */ void main(){ ALGraph G; createUDN(G); //printAdjList(G); printf("\n深度优先遍历(递归算法):\n"); DFSTraverseGraph(G); fflush(stdin);//清除残余后,后面再读入时不会出错 printf("\n深度优先遍历(非递归算法):\n"); DFSTraverseGraph2(G); fflush(stdin);//清除残余后,后面再读入时不会出错 printf("\n广度优先遍历(非递归算法):\n"); BFSTraverseGraph(G); }
相关文章推荐
- 数据结构学习之队列
- 6-5-树的双亲表示法-树和二叉树-第6章-《数据结构》课本源码-严蔚敏吴伟民版
- SPL的常用数据结构(2)
- 数据结构(1):顺序表
- 数据结构——线性表
- c语言实现求最短路径(迪杰斯特拉算法,《数据结构》算法7.15)
- 2015年大二上-数据结构-查找-1-(2)-分块查找
- 2015年大二上-数据结构-查找-1-(1)-线性表的折半查找
- 数据结构(16)--图的存储及实现
- 《数据结构》双向链表的创建
- HashMap的实现原理和底层数据结构
- 数据结构——单链表学习
- 数据结构-内核的双向循环链表-简单实现
- python数据结构
- 树的遍历
- 数据结构之二叉树
- 数据结构之集合
- 数据结构之栈
- 数据结构-图
- 数据结构——链表