(十一)图的深度遍历、广度遍历

利用邻接表对无向连通图进行深度遍历和广度遍历

1.深度遍历过程详解

 1.过程详解:首先选择一个节点开始遍历,选择与其相邻的一个节点,然后访问此节点啊,再次选择与先择选中的节点相邻的节点进行访问,重复此过程直到某一个节点的所有邻接点都已经被访问过。此时进行回溯,回溯到上一个访问的节点,如果它还存在未被访问的节点,那么则选中此节点并访问,如果没有则继续进行回溯,直到对整个无向连通图完成访问为止。
 2.点评:此过程借助邻接表的存储结构和递归算法进行遍历,思维过程都比较简单。
 3.怎么理解此过程?深度遍历顾名思义以深度为主,那么就需要看准一条路径一直走到无路可走为止(此时才进行回溯),因此才被称为深度

2.广度遍历过程详解

 1.过程详解:同样的随机选择一个节点作为遍历起点,访问此节点并将此节点所有的邻接节点压入队列。再对队列中的每一个元素如同起始节点般进行操作直到表头结点(邻接表存在一个表头)被遍历完成为止。
 2.点评:在严蔚敏所著的数据结构(c语言版)一书当中,广度遍历并不是所有节点同时进行的!!也就是说,在第一次把起始节点的所有邻接点访问并压入队列后,是先对队首的节点为中心进行广度遍历,弹出队首后再对新的队首进行广度遍历为访问的节点。因此这种做法不是以一个点为圆心的圆形扩散,而是先扩散一部分,再扩散一部分,最终呈现的效果是圆形扩散的!那么为什么要这样做呢?如果一次压入全部的邻接点,然后全部对其进行访问,再压入所有队列中节点的所有邻接点,就可能会出现节点被重复压入队列的情况!!因此就需要花费功夫去判断一个节点是否已经在栈中存在,这显然十分浪费效率。
3.怎么理解?广度不是看准一个方向走到底,而是以360°的范围进行圆形扩张。因此顾名思义,广度由此而来

3.代码实现

实现代码如下,一个注意点在遍历的过程中会用到两个和图有关的函数,

class Graph { //这里我只简写这两个函数,其他省略
	public:
		int FirstEdge(); //返回与表头节点直接相连的节点编号
		int NextEdge(); //对于非表头节点,我们使用它来求邻接表的下一个节点(若无节点则返回-1,以控制
						//退出循环
}

这两个函数需要补充到邻接表的存储结构的类当中作为功能函数被调用(此处为简便工作我予以省略)。
详细内容参照我的另一篇博客:邻接表、邻接矩阵存储图
下面看代码:(注释我尽可能多写)

#include <iostream>
#include <queue> 
using namespace std;
//-------------------------------图的DFS以及BFS ++ 邻接表存储结构--------------------------// 
#define MAXNUM 20
typedef struct Vertex { //展示节点结构
	int num; //序号
	struct Vertex *next;
} Vertex, *pVertex;
typedef struct TableHead { //展示表头结构
	int num;
	pVertex next;
} Tablehead, tablehead[MAXNUM];
class SearchGraph {
	public:
		int BegDfs(); void DFS(int); //实现深度遍历
		
		int BFS(); //实现广度遍历
		
		void visit(int num) {
			cout << num << endl; //输出遍历节点 
		}
		bool visited[MAXNUM]; //表头结点,示意是否已经被检测过
		queue<int> Q; //存储广度遍历一次所有的节点
		tablehead table; //表头
	private:
		int nodeNum, //节点数目
			edgeNum;
};
//----------------------------------------深度遍历--------------------------------------------// 
int SearchGraph::BegDfs() {
	for (int i = 0; i < nodeNum; ++i) visited[i] = false; //初始化标志访问数组 
	
	for (int i = 0; i < nodeNum; ++i) if (!visited[i]) DFS(i);
	
	return 0; //ok
}
void SearchGraph::DFS(int i) {
	visit(i); visited[i] = true; //访问该节点 
	
	for (int j = FirstEdge(i); j > 0; j = NextEdge(j)) if (!visited[j]) DFS(j); //借助邻接表递归 
}
//----------------------------------------广度遍历--------------------------------------------// 
int SearchGraph::BFS() {
	for (int i = 0; i < nodeNum; ++i) visited[i] = false; //重置使用过的标志访问数组

	pVertex p; int tmp;

	for (int i = 0; i < nodeNum; ++i) {
		if (!visited[i]) {
			visit(i); visited[i] = true; Q.push(i);
			while(!Q.empty()) { //针对连通图
				tmp = Q.front(); Q.pop();

				for (int j = FirstEdge(tmp); j > 0; j = NextEdge(j)) if (!visited[j - 1]) {visit(j - 1); visited[j - 1] = true; Q.push(j);}
			}
		}
	}
	return 0; //ok
}

int main() {
	
	SearchGraph G; //实例化对象

	G.BegDfs(); //进行深度遍历

	G.BFS(); // 进行广度遍历
	
	return 0;
}

4.一点注意

需要注意的是本篇博客的代码不仅适用于无向连通图,对于非连通图的遍历同样适用!!!
因为存在着对表头结点进行遍历这一操作,因此不用担心无法完成对全图的遍历。
(p.s:遍历一般只针对无向图,如果非要对有向图进行遍历,采用邻接表同样能实现遍历功能,但是此时表头结点所连的不再是与表头结点相邻接的点,而是以表头结点为弧尾的弧的弧首顶点

5.End

本来昨天就应该发出来这篇博客,但是我想把之前写的邻接表程序嵌入到这个程序里来并能最终运行得到截图给大家展示,后来想着这样实在是太过繁琐,并且不能很好的体现遍历的重心,也就作废了,拖到今天才发出来。
结尾箴言:路漫漫其修远兮,吾将上下而求索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值