图的遍历(详解DFS与BFS)

这篇具有很好参考价值的文章主要介绍了图的遍历(详解DFS与BFS)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

首先,我们来看一下涉及的知识点:

:图 G=(V,E) 由顶点集 V 和边集 E 组成。每条边对应一个点对 (v,w),其中 v,w 属于 V 。如果图中的点对是有序的,那么该图就是有向图,反之为无向图

邻接点:若顶点 v 与 w 之间存在一条边,则认为顶点 v 与 w 邻接。

:图中的每条边都可以对应一个数值,这种与边相关的数值称为权。

路径:在图 G 中,顶点 v1 到 vk 的路径是一个顶点序列 v1,v2,···,vk。

连通图:在无向图 G 中,若两个顶点之间存在路径,则认为这两个顶点是连通的。如果在无向图 G 中,任意两个顶点都是连通的,则称 G 是连通图。

完全图:若图中任意两个顶点之间都存在一条边,则该图为完全图。

稀疏图和稠密图:当图中边的数量比较少时,称该图为稀疏图;而当图接近完全图时,称该图为稠密图

接下来,我们进入正题:

1、深度优先遍历(DFS)

深度优先遍历类似于树的先序遍历。具体方法描述如下:

(1)从起始顶点 v 出发,首先访问顶点 v;
(2)选择一个与顶点 v 相邻接且没有被访问过的顶点 w 作为新的起始点,继续深度优先遍历,直到顶点 v 的所有邻接点都被访问过。

另外,若图不是连通图,则一次深度优先遍历只能访问到起始点所在连通分量中的所有顶点,而访问不到其它顶点。因此要从其它连通分量中选择起始点,继续深度优先遍历,才能将图中的所有顶点都访问一遍。为了保证在遍历过程中每个顶点只会被访问一次,我们借助一个辅助数组 Visit[] 来做一下标记。若 Visit[] 为1,则说明顶点 vi 已被访问过,若为0,则没有被访问过。

邻接矩阵的DFS代码:

# include <iostream>
# include <iomanip>
# define maxn 20
using namespace std;
typedef struct VexType {  //顶点类型
	int code;             //顶点编号
	char data;            //顶点信息
}VexType;
typedef struct Graph {     //邻接矩阵类型
	int arcs[maxn][maxn];  //邻接矩阵
	int vexnum, arcnum;    //顶点数和边的个数
	VexType vexs[maxn];    //顶点信息
	int type;              //邻接矩阵的类型(1.无向图 2.有向图)
}Graph;

int Visit[maxn] = { 0 };   //辅助数组,标记点是否已被访问过

void Create_Graph(Graph& G)   //创建
{
	cout << "请输入图的类型(1.无向图 2.有向图):";
	cin >> G.type;
	cout << "顶点的个数:";
	cin >> G.vexnum;
	cout << "请输入各顶点的名称:";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.vexs[i].data;
		G.vexs[i].code = i;  //按顺序为点编号
	}
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			G.arcs[i][j] = 0;  //邻接矩阵初始化,将所有元素的初始值设为0 
		}
	}
	cout << "请输入边的个数:";
	cin >> G.arcnum;
	cout << "请输入各边起点和终点的编号及权重:" << endl;
	int x, y, w;  //x:起始点,y:终点,w:权重
	for (int i = 0; i < G.arcnum; i++) {
		cin >> x >> y >> w;
		if (G.type == 1) {  //如果是无向图,对称边也要赋值为权重
			G.arcs[x][y] = w;
			G.arcs[y][x] = w;
		}
		else {
			G.arcs[x][y] = w;
		}
	}
}
  

void DFS(Graph G, int n)
{
	Visit[n] = 1;            //将起始点标记为已被访问过的状态
	cout << G.vexs[n].data;  //输出起始点的信息
	for (int i = 0; i < G.vexnum; i++) {
		//遍历起始点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
		//否则,以当前邻接点为新的起始点递归进行深度优先遍历
		if (!Visit[i] && (G.arcs[n][i] > 0 || G.arcs[i][n] > 0)) {
			DFS(G, i);
		}
	}
}

void DFS_Graph(Graph G)  //深度优先遍历,对每个顶点访问且仅访问一次
{
	for (int i = 0; i < G.vexnum; i++) {
		Visit[i] = 0;  //辅助数组初始化
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!Visit[i]) {  //对每一个未被访问过的顶点均调用一次深度优先遍历
			DFS(G, i);
		}
	}
}


int main()
{
	Graph G;
	Create_Graph(G);     //创建
	cout << "DFS:";
	DFS_Graph(G);        //深度优先遍历,对每个顶点访问且仅访问一次
	cout << endl;
	return 0;
}

邻接表的DFS代码:

# include <iostream>
# include <queue>
# define SIZE 20
# define NEWSIZE 20
using namespace std;
typedef struct ArcNode {  //边的结点结构类型
	int adjvex;           //该边的终点编号
	int weight;           //该边的权值
	struct ArcNode* nextarc;  //指向下一条边的指针
}ArcNode;
typedef struct VexNode {  //顶点结构
	char data;
	ArcNode* firstarc;    //指向第一条与该顶点有关的边的指针
}VexNode;
typedef struct Graph {    //邻接表结构类型
	VexNode* VNode;       //定义邻接表
	int vexnum, arcnum;   //顶点数和边的个数
	int type;             //图的种类
	int size;             //邻接表的大小
}Graph;

int* Visit; //辅助数组,标记点是否已被访问过

void Create_Graph(Graph& G)   //创建
{
	cout << "请输入图的类型(1.无向图 2.有向图):";
	cin >> G.type;
	cout << "顶点的个数:";
	cin >> G.vexnum;
	G.VNode = (VexNode*)malloc(SIZE * sizeof(VexNode));
	G.size = SIZE;
	while (G.size < G.vexnum) {   //根据点的个数动态分配空间
		G.VNode = (VexNode*)realloc(G.VNode, (G.size + NEWSIZE) * sizeof(VexNode));
		G.size += NEWSIZE;
	}
	Visit = (int*)malloc((G.size + 10) * sizeof(int));
	cout << "请输入各顶点的名称:";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.VNode[i].data;
		G.VNode[i].firstarc = NULL;  //邻接表初始化,所有单向链表均为空表
	}
	cout << "请输入边的个数:";
	cin >> G.arcnum;
	cout << "请输入各边起点和终点的编号及权重:" << endl;
	int x, y, w;    //x:起始点,y:终点,w:权重
	ArcNode* p, * q;
	for (int i = 0; i < G.arcnum; i++) {
		cin >> x >> y >> w;
		p = (ArcNode*)malloc(sizeof(ArcNode)); //创建一个用于存放当前边的结点p
		p->nextarc = NULL;
		p->adjvex = y;
		p->weight = w;
		q = G.VNode[x].firstarc;
		//将边按顺序插入到链表末尾
		if (q == NULL) {   
			G.VNode[x].firstarc = p;
		}
		else {
			while (q->nextarc != NULL) { 
				q = q->nextarc;
			}
			q->nextarc = p;
		}
		if (G.type == 1) {    //如果是无向图,要再创建一个表示对称边的结点p
			p = (ArcNode*)malloc(sizeof(ArcNode));
			p->nextarc = NULL;
			p->adjvex = x;
			p->weight = w;
			q = G.VNode[y].firstarc;
			if (q == NULL) {
				G.VNode[y].firstarc = p;
			}
			else {
				while (q->nextarc != NULL) {
					q = q->nextarc;
				}
				q->nextarc = p;
			}
		}
	}
}

void DFS(Graph G, int n)
{
	Visit[n] = 1;             //将起始点标记为已被访问过的状态
	cout << G.VNode[n].data;  //输出起始点的信息
	ArcNode* p = G.VNode[n].firstarc;
	while (p) {
		//遍历起始点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
		//否则,以当前邻接点为新的起始点递归进行深度优先遍历
		if (!Visit[p->adjvex]) {  
			DFS(G, p->adjvex);
		}
		p = p->nextarc;
	}
}

void DFS_Graph(Graph G)  //深度优先遍历,对每个顶点访问且仅访问一次
{
	for (int i = 0; i < G.vexnum; i++) {
		Visit[i] = 0;  //辅助数组初始化
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!Visit[i]) {  //对每一个未被访问过的顶点均调用一次深度优先遍历
			DFS(G, i);
		}
	}
}


int main()
{
	Graph G;
	Create_Graph(G);     //创建
	cout << "DFS:";
	DFS_Graph(G);        //深度优先遍历,对每个顶点访问且仅访问一次
	cout << endl;
	return 0;
}

 运行结果:

请输入图的类型(1.无向图 2.有向图):1
顶点的个数:8
请输入各顶点的名称:A B C D E F G H
请输入边的个数:9
请输入各边起点和终点的编号及权重:
0 1 3
0 2 12
1 3 5
1 4 6
2 3 7
2 4 10
5 6 9
5 7 4
6 7 13
DFS:ABDCEFGH

对邻接矩阵DFS的时间复杂度为 O(n^2),对邻接表DFS的时间复杂度为 O(n+e)( n 是图中顶点的个数,e 是边的个数),但邻接表的创建要较为复杂。

2、广度优先遍历(BFS)

广度优先遍历类似于树的层次遍历。具体方法描述如下:

(1)从起始顶点 v 出发,首先访问顶点 v;

(2)依次访问 v 的所有没有被访问过的邻接点 w1,w2,···,wk;

(3)按照 w1,w2,···,wk 的次序,访问它们各自所有未被访问过的邻接点;

(4)以此类推,直到图中所有与起始点 v 连通的顶点都被访问过为止。

同样,若图不是连通图,则一次广度优先遍历只能访问到与起始点连通的那些顶点,而访问不到其它顶点。因此要从其它连通分量中选择起始点,继续广度优先遍历,才能将图中的所有顶点都访问一遍。为了保证在遍历过程中每个顶点只会被访问一次,我们也要借助一个辅助数组 Visit[] 来做一下标记。若 Visit[] 为1,则说明顶点 vi 已被访问过,若为0,则没有被访问过。

 

邻接矩阵的BFS代码:

# include <iostream>
# include <iomanip>
# include <queue>
# define maxn 20
using namespace std;
typedef struct VexType {  //顶点类型
	int code;             //顶点编号
	char data;            //顶点信息
}VexType;
typedef struct Graph {     //邻接矩阵类型
	int arcs[maxn][maxn];  //邻接矩阵
	int vexnum, arcnum;    //顶点数和边的个数
	VexType vexs[maxn];    //顶点信息
	int type;              //邻接矩阵的类型(1.无向图 2.有向图)
}Graph;

int Visit[maxn] = { 0 };   //辅助数组,标记点是否已被访问过

void Create_Graph(Graph& G)   //创建
{
	cout << "请输入图的类型(1.无向图 2.有向图):";
	cin >> G.type;
	cout << "顶点的个数:";
	cin >> G.vexnum;
	cout << "请输入各顶点的名称:";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.vexs[i].data;
		G.vexs[i].code = i;  //按顺序为点编号
	}
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			G.arcs[i][j] = 0;  //邻接矩阵初始化,将所有元素的初始值设为0 
		}
	}
	cout << "请输入边的个数:";
	cin >> G.arcnum;
	cout << "请输入各边起点和终点的编号及权重:" << endl;
	int x, y, w;  //x:起始点,y:终点,w:权重
	for (int i = 0; i < G.arcnum; i++) {
		cin >> x >> y >> w;
		if (G.type == 1) {  //如果是无向图,对称边也要赋值为权重
			G.arcs[x][y] = w;
			G.arcs[y][x] = w;
		}
		else {
			G.arcs[x][y] = w;
		}
	}
}


void BFS(Graph G, int n)
{
	queue<int>Q;
	Visit[n] = 1;            //将起始点标记为已被访问过的状态
	cout << G.vexs[n].data;  //先输出起始点信息
	Q.push(n);               //起始点入队
	while (!Q.empty()) {     
		n = Q.front();       //队头元素出队
		Q.pop();
		for (int i = 0; i < G.vexnum; i++) {
			//遍历当前队头结点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
			//否则入队,并标记为已访问状态,防止重复入队
			if (!Visit[i] && (G.arcs[n][i] > 0 || G.arcs[i][n] > 0)) {
				Visit[i] = 1;
				cout << G.vexs[i].data;
				Q.push(i);
			}
		}
	}
}

void BFS_Graph(Graph G)  //广度优先遍历,对每个顶点访问且仅访问一次
{
	for (int i = 0; i < G.vexnum; i++) {
		Visit[i] = 0;   //辅助数组初始化
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!Visit[i]) {  //对每一个未被访问过的顶点均调用一次广度优先遍历
			BFS(G, i);
		}
	}
}


int main()
{
	Graph G;
	Create_Graph(G);     //创建
	cout << "BFS:";
	BFS_Graph(G);        //广度优先遍历,对每个顶点访问且仅访问一次
    cout<<endl;
	return 0;
}

 

邻接表的BFS代码:

# include <iostream>
# include <queue>
# define SIZE 20
# define NEWSIZE 20
using namespace std;
typedef struct ArcNode {  //边的结点结构类型
	int adjvex;           //该边的终点编号
	int weight;           //该边的权值
	struct ArcNode* nextarc;  //指向下一条边的指针
}ArcNode;
typedef struct VexNode {  //顶点结构
	char data;
	ArcNode* firstarc;    //指向第一条与该顶点有关的边的指针
}VexNode;
typedef struct Graph {    //邻接表结构类型
	VexNode* VNode;       //定义邻接表
	int vexnum, arcnum;   //顶点数和边的个数
	int type;             //图的种类
	int size;             //邻接表的大小
}Graph;

int* Visit; //辅助数组,标记点是否已被访问过

void Create_Graph(Graph& G)   //创建
{
	cout << "请输入图的类型(1.无向图 2.有向图):";
	cin >> G.type;
	cout << "顶点的个数:";
	cin >> G.vexnum;
	G.VNode = (VexNode*)malloc(SIZE * sizeof(VexNode));
	G.size = SIZE;
	while (G.size < G.vexnum) {   //根据点的个数动态分配空间
		G.VNode = (VexNode*)realloc(G.VNode, (G.size + NEWSIZE) * sizeof(VexNode));
		G.size += NEWSIZE;
	}
	Visit = (int*)malloc((G.size + 10) * sizeof(int));
	cout << "请输入各顶点的名称:";
	for (int i = 0; i < G.vexnum; i++) {
		cin >> G.VNode[i].data;
		G.VNode[i].firstarc = NULL;  //邻接表初始化,所有单向链表均为空表
	}
	cout << "请输入边的个数:";
	cin >> G.arcnum;
	cout << "请输入各边起点和终点的编号及权重:" << endl;
	int x, y, w;    //x:起始点,y:终点,w:权重
	ArcNode* p, * q;
	for (int i = 0; i < G.arcnum; i++) {
		cin >> x >> y >> w;
		p = (ArcNode*)malloc(sizeof(ArcNode)); //创建一个用于存放当前边的结点p
		p->nextarc = NULL;
		p->adjvex = y;
		p->weight = w;
		q = G.VNode[x].firstarc;
		//将边按顺序插入到链表末尾
		if (q == NULL) {   
			G.VNode[x].firstarc = p;
		}
		else {
			while (q->nextarc != NULL) { 
				q = q->nextarc;
			}
			q->nextarc = p;
		}
		if (G.type == 1) {    //如果是无向图,要再创建一个表示对称边的结点p
			p = (ArcNode*)malloc(sizeof(ArcNode));
			p->nextarc = NULL;
			p->adjvex = x;
			p->weight = w;
			q = G.VNode[y].firstarc;
			if (q == NULL) {
				G.VNode[y].firstarc = p;
			}
			else {
				while (q->nextarc != NULL) {
					q = q->nextarc;
				}
				q->nextarc = p;
			}
		}
	}
}


void BFS(Graph G, int n)
{
	queue<int>Q;
	Visit[n] = 1;             //将起始点标记为已被访问过的状态
	cout << G.VNode[n].data;  //先输出起始点信息
	Q.push(n);                //起始点入队
	while (!Q.empty()) {
		n = Q.front();        //队头元素出队
		Q.pop();
		ArcNode* p = G.VNode[n].firstarc;
		while (p) {
			//遍历当前队头结点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
			//否则入队,并标记为已访问状态,防止重复入队
			if (!Visit[p->adjvex]) {
				Visit[p->adjvex] = 1;
				cout << G.VNode[p->adjvex].data;
				Q.push(p->adjvex);
			}
			p = p->nextarc;
		}
	}
}

void BFS_Graph(Graph G)  //广度优先遍历,对每个顶点访问且仅访问一次
{
	for (int i = 0; i < G.vexnum; i++) {
		Visit[i] = 0;   //辅助数组初始化
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!Visit[i]) {  //对每一个未被访问过的顶点均调用一次广度优先遍历
			BFS(G, i);
		}
	}
}


int main()
{
	Graph G;
	Create_Graph(G);     //创建
	cout << "BFS:";
	BFS_Graph(G);        //广度优先遍历,对每个顶点访问且仅访问一次
	cout << endl;
	return 0;
}

运行结果:

请输入图的类型(1.无向图 2.有向图):1
顶点的个数:8
请输入各顶点的名称:A B C D E F G H
请输入边的个数:9
请输入各边起点和终点的编号及权重:
0 1 3
0 2 12
1 3 5
1 4 6
2 3 7
2 4 10
5 6 9
5 7 4
6 7 13
BFS:ABCDEFGH

相似的,对邻接矩阵BFS的时间复杂度为 O(n^2),对邻接表BFS的时间复杂度为 O(n+e)( n 是图中顶点的个数,e 是边的个数)。

以上就是我这次的全部学习成果,很高兴能与大家分享。文章来源地址https://www.toymoban.com/news/detail-778625.html

到了这里,关于图的遍历(详解DFS与BFS)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构与算法基础-学习-24-图的遍历之DFS(深度优先搜索)和BFS(广度优先搜索)

    目录 一、遍历定义 二、遍历实质 三、DFS 四、BFS 五、宏定义 六、自定义类型 七、函数实现 1、DFS(邻接矩阵实现) 2、DFS(邻接表实现) 3、BFS(邻接矩阵实现) 4、BFS(邻接表实现) 5、打印邻接矩阵遍历顺序  6、打印邻接表遍历顺序 八、遍历算法效率分析 1、DFS 2、BFS 九

    2024年02月03日
    浏览(59)
  • 数据结构实验6 :图的存储与遍历(邻接矩阵的深度优先遍历DFS和邻接表的广度优先遍历BFS)

    利用邻接矩阵存储无向图,并从0号顶点开始进行深度优先遍历。 输入第一行是两个整数n1 n2,其中n1表示顶点数(则顶点编号为0至n1-1),n2表示图中的边数。 之后有n2行输入,每行输入表示一条边,格式是“顶点1 顶点2”,把边插入图中。 例如: 4 4 0 1 1 3 0 3 0 2 先输出存储

    2024年02月09日
    浏览(54)
  • 图的遍历(广度优先遍历BFS,深度优先遍历DFS)

    目录 图的遍历概念: 图的广度优先遍历(BFS): 代码实现如下: 测试如下: 注意: 图的深度优先遍历(DFS): 代码实现如下: 测试如下: 总代码: 结语: 给定一个图G和其中任意一个顶点v0,从v0出发,沿着图中各边访问图中的所有顶点,且每个顶点仅被遍历一次。\\\"遍

    2024年02月21日
    浏览(44)
  • 图的遍历(搜索)算法(深度优先算法DFS和广度优先算法BFS)

    从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次。(连通图与非连通图) 1、访问指定的起始顶点; 2、若当前访问的顶点的邻接顶点有未被访问的,则任选一个访问之;反之,退回到最近访问过的顶点;直到与起始顶点相通的全部顶点都访问完毕; 3、若

    2024年01月17日
    浏览(36)
  • 数据结构上机实验——图的实现(以无向邻接表为例)、图的深度优先搜索(DFS)、图的广度优先搜索(BFS)

      图采用邻接表存储结构,编程实现图的深度优先搜索和广度优先搜索算法。              2.1创建图 2.1.1定义图的顶点、边及类定义   我们定义一个 邻接表类(ALGraph) 。这里实现一些基础的数据结构。要注意结构体的嵌套。    Edge: 用于表示图中的边

    2024年02月04日
    浏览(42)
  • 图的遍历——深度优先搜索(DFS)与广度优先搜索(BFS)(附带C语言源码)

    个人主页:【😊个人主页】 系列专栏:【❤️数据结构与算法】 学习名言:天子重英豪,文章教儿曹。万般皆下品,惟有读书高——《神童诗劝学》 第一章 ❤️ 学前知识 第二章 ❤️ 单向链表 第三章 ❤️ 递归 … 在此之前我们学习过了图的一些基本概念,如同在二叉树

    2024年02月06日
    浏览(52)
  • 数据结构与算法 | 深搜(DFS)与广搜(BFS)

    在查找二叉树某个节点时,如果把二叉树所有节点理解为解空间,待找到那个节点理解为满足特定条件的解,对此解答可以抽象描述为: 在解空间中搜索满足特定条件的解 ,这其实就是搜索算法(Search)的一种描述。当然也有其他描述,比如是“指一类用于在数据集合中查

    2024年02月08日
    浏览(24)
  • 【算法手札】深入理解宽度遍历(bfs)和深度遍历(dfs)搜索

        算法的重要性不言而喻,现在我们的生活也已经离不开各种算法,一个好的算法能大大提高程序的运行效率,是学习编程的一个重要模块,而遍历算法也是算法里的一个大的模块,今天我们一起来学习一下深度遍历算法(depth first search)和 广度优先遍历(broad first searc

    2024年02月21日
    浏览(34)
  • 【数据结构——有向图】有环无环判定、拓扑排序(DFS、BFS)

    有向图(Directed Graph),也被称为有向图形或方向图,是一种图的类型。在有向图中,图中的边具有方向,从一个顶点指向另一个顶点。 在有向图中,每个顶点表示一个实体,而有向边则表示实体之间的关系或连接。这种有方向性的边表明了连接的起点和终点之间的单向关系

    2024年02月09日
    浏览(39)
  • 【算法基础:搜索与图论】3.2 树与图的dfs和bfs

    要学会建树、建图的通用方法。 dfs 和 bfs 的代码框架。 https://www.acwing.com/problem/content/848/ 在 dfs 的过程中,统计各个节点作为断点时的连通块最大值。 https://www.acwing.com/problem/content/849/ 看到最短距离就可以想到使用宽搜。 注意! :题目中说明了 a 和 b 表示存在一条从 a 走到

    2024年02月16日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包