文章目录

  • 定义
  • 深度优先遍历
  • 广度优先遍历
  • 测试代码

定义

从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫做图的遍历(Traversing Graph)。

深度优先遍历

深度优先遍历(Depth_First_Search),也有称深度优先搜索,简称DFS。

为了理解深度优先遍历,用一个小游戏来说明。
假设你要完成一个任务,要求在如图1-1中的左图中这样的一个迷宫中,从顶点A开始要走遍所有的图顶点并做上标记,注意不是简单地看着这样的平面图走哦。而是如同现实般地只有高墙和通道的迷宫中去完成任务。

图1-1

很显然是需要策略的,现在使用深度优先遍历来完成。

首先从顶点A开始,做上表示做过的记号之后,前面有两条路,通向B和F,我们给自己一个原则,在没有碰到重复顶点的情况下,始终是向右手边走,于是走到了顶点B,整个行走过程,可参看图1-1的右图。此时发现有三个分支,分别通往顶点C,I,G右手通行原则,我们走到了顶点C,就这样,我们一直顺着右手通道走,一直走到顶点F,当我们依然选择右手通道走过去时,发现走回到顶点A了。此时我们退回到顶点F,走向从右数的第二条通道,到了G点,他有三条通道,B和D是已经走过的,于是走到H,当我们走到H时,发现两条通道D和E,会发现都已经走过了。

此时我们没有遍历完所有的顶点,在顶点H处,在无通道没走过,返回到G,也无从未走过的通道,回到F,没有通道。回到E,有一条通向H的通道,验证后也是走过的,返回到D,此时还有三条没有走过,一条条走,H,G,I,继续返回,直到回到顶点A,确认你已经完成了遍历任务。找到了9个顶点。

深度优点遍历其实就是递归的过程,如果在仔细点,会发现转换成1-1的右图之后,就像是一棵树的前序遍历。从图中某个顶点V开始,访问此顶点,然后从V的未被访问的邻接点出发深度优先遍历图,直到图中所有和V有路径相通的顶点都被访问到。 事实上,我们这里说的是连通图。对于非连通图,只需要对它的连通分量分别进行深度优点遍历,即在先前一个顶点进行一次深度优先遍历后,如图中有顶点没有被访问,则选图中一个未曾被访问的顶点做起始点,重复上述过程,直到图中所有顶点都被访问到为止。

如果我们使用的是邻接矩阵的方式,则代码如下:

typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
Boolean visited[MAXVEX]; /* 访问标志的数组 *//* 邻接矩阵的深度优先递归算法 */
void DFS(MGraph G, int i)
{int j;visited[i] = TRUE;printf("%c ", G.vexs[i]);/* 打印顶点,也可以其它操作 */for(j = 0; j < G.numVertexes; j++)if(G.arc[i][j] == 1 && !visited[j])DFS(G, j);/* 对为访问的邻接顶点递归调用 */
}/* 邻接矩阵的深度遍历操作 */
void DFSTraverse(MGraph G)
{int i;for(i = 0; i < G.numVertexes; i++)visited[i] = FALSE; /* 初始所有顶点状态都是未访问过状态 */for(i = 0; i < G.numVertexes; i++)if(!visited[i]) /* 对未访问过的顶点调用DFS,若是连通图,只会执行一次 */ DFS(G, i);
}

如果图结构是邻接表结构,其DFSTraverse函数的代码几乎是相同的,只是在递归函数中因为数组换成链表而有不同。

typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
Boolean visited[MAXVEX]; /* 访问标志的数组 *//* 邻接矩阵的深度优先递归算法 */
void DFS(MGraph GL, int i)
{EdgeNode *p;visited[i] = TRUE;printf("%c ", GL->adjList[i].data);/* 打印顶点,也可以其它操作 */p=GL->adjList[i]firstedge;while(p){    if(!visited[p->adjvex])DFS(GL.P->adjvex);p=p->next;}
}/* 邻接矩阵的深度遍历操作 */
void DFSTraverse(MGraphList GL)
{int i;for(i = 0; i < GL.numVertexes; i++)visited[i] = FALSE; /* 初始所有顶点状态都是未访问过状态 */for(i = 0; i < GL.numVertexes; i++)if(!visited[i]) /* 对未访问过的顶点调用DFS,若是连通图,只会执行一次 */ DFS(GL, i);

广度优先遍历

广度优先遍历(Breafth_First_Search),又称广度优先搜索,简称BFS。
如果说图的深度优先遍历类似于树的前序遍历,那么图的广度优先遍历就类似于树的层序遍历了。我们将图1-2的第一幅稍微变形,变形原则就是顶点A放置在最上第一层,让与他有边的顶点B,F成为第二层,再让B和F有边的顶点C,I,G,E为第三层,在将这四个顶点有边的D,H放在第四层,如图1-2第二幅图所示、此时在视觉上感觉图的形状发生了变化,其实顶点和边的关系还是完全相同的。

图1-2

/* 邻接矩阵的广度遍历算法 */
void BFSTraverse(MGraph G)
{int i, j;Queue Q;for(i = 0; i < G.numVertexes; i++)visited[i] = FALSE;InitQueue(&Q);        /* 初始化一辅助用的队列 */for(i = 0; i < G.numVertexes; i++)  /* 对每一个顶点做循环 */{if (!visited[i])  /* 若是未访问过就处理 */{visited[i]=TRUE;       /* 设置当前顶点访问过 */printf("%c ", G.vexs[i]);/* 打印顶点,也可以其它操作 */EnQueue(&Q,i);       /* 将此顶点入队列 */while(!QueueEmpty(Q))  /* 若当前队列不为空 */{DeQueue(&Q,&i);  /* 将队对元素出队列,赋值给i */for(j=0;j<G.numVertexes;j++) { /* 判断其它顶点若与当前顶点存在边且未访问过  */if(G.arc[i][j] == 1 && !visited[j]) { visited[j]=TRUE;         /* 将找到的此顶点标记为已访问 */printf("%c ", G.vexs[j]);  /* 打印顶点 */EnQueue(&Q,j);                /* 将找到的此顶点入队列  */} } }}}
}

测试代码

#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0typedef int Status;  /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */typedef char VertexType; /* 顶点类型应由用户定义 */
typedef int EdgeType; /* 边上的权值类型应由用户定义 */#define MAXSIZE 9 /* 存储空间初始分配量 */
#define MAXEDGE 15
#define MAXVEX 9
#define INFINITY 65535typedef struct
{VertexType vexs[MAXVEX]; /* 顶点表 */EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */int numVertexes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;/* 用到的队列结构与函数********************************** *//* 循环队列的顺序存储结构 */
typedef struct
{int data[MAXSIZE];int front;       /* 头指针 */int rear;      /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}Queue;/* 初始化一个空队列Q */
Status InitQueue(Queue *Q)
{Q->front=0;Q->rear=0;return  OK;
}/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
Status QueueEmpty(Queue Q)
{ if(Q.front==Q.rear) /* 队列空的标志 */return TRUE;elsereturn FALSE;
}/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(Queue *Q,int e)
{if ((Q->rear+1)%MAXSIZE == Q->front)  /* 队列满的判断 */return ERROR;Q->data[Q->rear]=e;         /* 将元素e赋值给队尾 */Q->rear=(Q->rear+1)%MAXSIZE;/* rear指针向后移一位置, *//* 若到最后则转到数组头部 */return  OK;
}/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(Queue *Q,int *e)
{if (Q->front == Q->rear)           /* 队列空的判断 */return ERROR;*e=Q->data[Q->front];               /* 将队头元素赋值给e */Q->front=(Q->front+1)%MAXSIZE;   /* front指针向后移一位置, *//* 若到最后则转到数组头部 */return  OK;
}
/* ****************************************************** */void CreateMGraph(MGraph *G)
{int i, j;G->numEdges=15;G->numVertexes=9;/* 读入顶点信息,建立顶点表 */G->vexs[0]='A';G->vexs[1]='B';G->vexs[2]='C';G->vexs[3]='D';G->vexs[4]='E';G->vexs[5]='F';G->vexs[6]='G';G->vexs[7]='H';G->vexs[8]='I';for (i = 0; i < G->numVertexes; i++)/* 初始化图 */{for ( j = 0; j < G->numVertexes; j++){G->arc[i][j]=0;}}G->arc[0][1]=1;G->arc[0][5]=1;G->arc[1][2]=1; G->arc[1][8]=1; G->arc[1][6]=1; G->arc[2][3]=1; G->arc[2][8]=1; G->arc[3][4]=1;G->arc[3][7]=1;G->arc[3][6]=1;G->arc[3][8]=1;G->arc[4][5]=1;G->arc[4][7]=1;G->arc[5][6]=1; G->arc[6][7]=1; for(i = 0; i < G->numVertexes; i++){for(j = i; j < G->numVertexes; j++){G->arc[j][i] =G->arc[i][j];}}}Boolean visited[MAXVEX]; /* 访问标志的数组 *//* 邻接矩阵的深度优先递归算法 */
void DFS(MGraph G, int i)
{int j;visited[i] = TRUE;printf("%c ", G.vexs[i]);/* 打印顶点,也可以其它操作 */for(j = 0; j < G.numVertexes; j++)if(G.arc[i][j] == 1 && !visited[j])DFS(G, j);/* 对为访问的邻接顶点递归调用 */
}/* 邻接矩阵的深度遍历操作 */
void DFSTraverse(MGraph G)
{int i;for(i = 0; i < G.numVertexes; i++)visited[i] = FALSE; /* 初始所有顶点状态都是未访问过状态 */for(i = 0; i < G.numVertexes; i++)if(!visited[i]) /* 对未访问过的顶点调用DFS,若是连通图,只会执行一次 */ DFS(G, i);
}/* 邻接矩阵的广度遍历算法 */
void BFSTraverse(MGraph G)
{int i, j;Queue Q;for(i = 0; i < G.numVertexes; i++)visited[i] = FALSE;InitQueue(&Q);        /* 初始化一辅助用的队列 */for(i = 0; i < G.numVertexes; i++)  /* 对每一个顶点做循环 */{if (!visited[i])  /* 若是未访问过就处理 */{visited[i]=TRUE;       /* 设置当前顶点访问过 */printf("%c ", G.vexs[i]);/* 打印顶点,也可以其它操作 */EnQueue(&Q,i);       /* 将此顶点入队列 */while(!QueueEmpty(Q))  /* 若当前队列不为空 */{DeQueue(&Q,&i);  /* 将队对元素出队列,赋值给i */for(j=0;j<G.numVertexes;j++) { /* 判断其它顶点若与当前顶点存在边且未访问过  */if(G.arc[i][j] == 1 && !visited[j]) { visited[j]=TRUE;         /* 将找到的此顶点标记为已访问 */printf("%c ", G.vexs[j]);  /* 打印顶点 */EnQueue(&Q,j);                /* 将找到的此顶点入队列  */} } }}}
}int main(void)
{    MGraph G;CreateMGraph(&G);printf("\n深度遍历:");DFSTraverse(G);printf("\n广度遍历:");BFSTraverse(G);return 0;
}

【数据结构】之图的遍历(C语言)相关推荐

  1. 图的遍历c语言数据结构实验报告,数据结构图的遍历实验报告.doc

    数据结构图的遍历实验报告.doc 实 验 报 告课程名称 数据结构实验名称 图的遍历姓 名专 业 计算机科学与技术班 级 计算机学 号成绩计算机科学与技术学院实验教学中心2015 年 11 月 20 ...

  2. 图的遍历c语言数据结构实验报告,数据结构实验报告--图的遍历

    江 西 理 工 大 学 数据结构 实验报告 实验名称 图的遍历 日期 2014-12-01 专业班级 计算机(中加)131班 地点 信息学院621 实验人 王鹏伟 学号 同组人 单独完成 152013 ...

  3. c++ 遍历list_数据结构之图的遍历,一篇文章get全部考点

    图的概念 举个例子,假设你的微信朋友圈中有若干好友:张三.李四.王五.赵六.七大姑.八大姨. 而你七大姑的微信号里,又有若干好友:你.八大姨.Jack.Rose. 微信中,许许多多的用户组成了一个多对 ...

  4. 【数据结构】图的遍历(BFS和DFS)

    图的遍历 图的遍历是指从图中的某一顶点出发,按照某种搜索方式沿着途中的边对图中所有顶点访问一次且仅访问一次.图的遍历主要有两种算法:广度优先搜索和深度优先搜索. 广度优先遍历BFS 广度优先遍历(BF ...

  5. 数据结构之图的遍历:深度优先遍历(DFS)

    图的遍历:深度优先遍历 思维导图: 深度优先遍历的原理: 深度优先遍历的代码实现: 深度优先遍历的性能: 深度优先生成树: 遍历与连通性的关系: 思维导图: 深度优先遍历的原理: ps: 实现方法是递 ...

  6. 数据结构之图的遍历:广度优先遍历(BFS)

    图的遍历:广度优先遍历 思维导图: 广度优先遍历的原理: 广度优先遍历的代码实现: 广度优先遍历的性能分析: 无权图单源最短路径问题: 广度优先生成树: 思维导图: 广度优先遍历的原理: 类似与树的层 ...

  7. 图的遍历(C语言,邻接表存储的图 - DFS,邻接矩阵存储的图 - BFS)

    邻接表存储的图 - DFS /* 邻接表存储的图 - DFS */void Visit( Vertex V ) {printf("正在访问顶点%d\n", V); }/* Visi ...

  8. 数据结构(廿五) -- C语言版 -- 图 - 图的遍历 -- 邻接矩阵 - 深度/广度优先遍历/搜索(DFS、BFS)

    内容预览 零.读前说明 一.概 述 二.深度优先遍历(DFS) 2.1.无向图的遍历过程 2.2.有向图的遍历过程 2.3.总结说明 2.4.实现源代码 三.广度优先遍历(BFS) 3.1.广度优先的 ...

  9. 数据结构(廿六) -- C语言版 -- 图 - 图的遍历 -- 邻接表 - 深度/广度优先遍历/搜索(DFS、BFS)

    内容预览 零.读前说明 一.深度优先遍历 1.1.深度优先的遍历过程 1.2.深度优先的遍历实现代码 二.广度优先遍历 2.1.广度优先的遍历过程 2.2.广度优先的遍历实现代码 三.源码测试效果 3 ...

  10. C语言《数据结构》——图的概念和创建,遍历

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 例如:随着计算机网络的发展,编程成了一种很常见且重要的职业,学好编程就要学好数据结构,下面将介绍数据结构中的图结构. ...

最新文章

  1. eclipse中egit插件使用
  2. CF715B. Complete The Graph
  3. Jquery ajax调用后台aspx后台文件方法(不是ashx)
  4. Java中的List接口实现类LinkedList
  5. 前端学习(1373):构建模块化路由2
  6. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型
  7. 直接说,我要怎样才能做到年薪 50 万?
  8. android最简单的更换主题,教你更换Android手机主题
  9. LeetCode:35. Search Insert Position(Easy)
  10. Java自动化测试——打开浏览器
  11. NPDP考试地点在哪?
  12. 双系统装完只能u盘启动_双系统启动引导修复 双系统启动引导设置教程
  13. 2.垃圾收集器与内存分配策略
  14. 【一】欧式空间、欧式变换
  15. 美国队长的工资 python代码-Python | 用Python画个美队盾牌送给你
  16. Android WebRTC 入门教程(一) -- 使用相机
  17. 【记录】深度学习之蒸馏法训练网络
  18. 个人开发者的白piao云服务器
  19. matlab 积分 例子,[Matlab]使用arrayfun对矩阵表达式积分的例子
  20. 微信小程序集成jenkins自动打码

热门文章

  1. JS在数组对象中添加新字段
  2. C/C++基于朋友圈的商品推荐系统
  3. LINUX ROUTE命令详解-2
  4. 学java有前途吗?方兴未艾!
  5. 基于GRNN网络和小波变换的ECG信号睡眠监测matlab仿真
  6. form表单提交数据的两种方式——submit直接提交、AJAX提交
  7. Java-----投票系统
  8. 基于SSM的小区物业管理系统JAVA【数据库设计、论文、源码、开题报告】
  9. Java使用aspose批量将PDF转为word
  10. 新手nvm npm 卸载不用依赖包,项识别为 cmdlet、函数、脚本文件,等命令集合