基于图的邻接表的深度优先遍历(DFS)和广度优先遍历(BFS)

深度优先遍历(Depth-First Search):
用深度优先搜索策略遍历一个图类似于树的前序遍历,对于一个图G=(V,E),首先将图中的每一个顶点都标记为未访问,然后选取一个源点v,将其标为已访问,再递归地用深度优先搜索方法,依次搜索该点的所有邻接点w。若w未曾访问,则以w为源点继续进行深度优先遍历,如果从v出发的所有路的顶点都已被访问过,则从v的搜索过程结束。此时如果图中还有未被访问的顶点(该图有多个连通分量或强连通分量),则再任选一个未被访问过的顶点,从这个顶点开始新的搜索,直到V中所有顶点都已被访问过为止。
用深度优先搜索方法遍历图称为图的深度优先遍历

例如,对于这个无向图:
在这里插入图片描述
**深度优先搜索的访问顺序与图的邻接表存储状态有关,由于图的邻接表存储不是唯一的,因此对于同一个图,DFS的结果可能也是不同的。但是邻接矩阵就唯一了。

例如我采取不同的输入方式,就得到了不同的结果:
(第一行为点的数量,第二行为边的数量,第三行为各个顶点值,接下来的有序对为每条边对应的顶点)
(代码将在稍后给出)
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由此可见,邻接表的建立方式不同(边的输入方式不同),DFS的结果也不同。

广度优先遍历(Breadth-First Search):
用广度优先搜索策略遍历一个图类似于树的层次遍历,对于一个图G=(V,E),从图中的某个源点v出发,在访问了顶点v之后,接着就尽可能横向搜索v的所有邻接点。在依次访问v的各个未被访问过的邻接点w1、w2 … wk之后,分别从这些邻接点出发依次访问与w1、w2 … wk邻接的所有未曾访问过的顶点。依此类推,直至图中所有和源点v有路径相通的顶点都已访问过为止,此时从v开始的搜索过程结束。若G是连通图,则遍历完成;否则,在G中另选一个尚未访问过的顶点作为新的源点继续上述搜索过程,直至G中所有顶点均被访问完为止。
用广度优先搜索方法遍历图称为图的广度优先遍历

同理,BFS也会因为邻接表的建立差异而导致结果不同,如图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

接下来给出源码:
(其中头文件"ljb.h"是邻接表的建立过程,->代码在这里<-)

/*main.c*/
#include "ljb.h"

int visited[M];                              //标志数组,为1代表已访问过,为0代表未访问过

void dfs(LinkedGraph g, int i) {
	EdgeNode* p;
	printf("%c ", g.adjlist[i].vertex);       //访问源点
	/*此时i==0*/
	visited[i] = 1;
	/*第0个结点的标志为1,表示第0个结点已经访问过了*/
	p = g.adjlist[i].FirstEdge;
	/*p为第0个结点的头指针域,指向下一个邻接表结点(由你的输入顺序决定)*/
	while (p) {                            //从p的邻接点进行深度优先搜索
	/*p不为空*/
		if (!visited[p->adjvex]) {
		/*p的邻接表节点存在且未被访问(标志数组为0)*/
			dfs(g, p->adjvex);
			/*继续以p的邻接点为源点进行深度优先遍历*/
		}
		p = p->next;
	}
}

void DfsTraverse(LinkedGraph g) {
	int i;
	for (i = 0; i < g.n; i++) {
		visited[i] = 0;                   //初始化标志数组
	}
	for (i = 0; i < g.n; i++) {
		if (!visited[i]) {                //未访问过
			dfs(g, i);
		}
	}
	printf("\n");
}

void bfs(LinkedGraph g, int i) {
	int j;
	EdgeNode* p;
	int queue[M], front, rear;               //初始化FIFO队列
	front = rear = 0;                       //初始化空队列
	printf("%c ", g.adjlist[i].vertex);       //访问源点
	/*此时i==0*/
	visited[i] = 1;
	/*第0个结点的标志为1,表示第0个结点已经访问过了*/
	queue[rear++] = i;                      //被访问节点进队
	/*第0个结点进队,此时队列中只有一个0*/
	while (rear > front) {                  //当队列非空时,执行以下循环
		j = queue[front++];                 //出队
		/*此时j==0,0出队*/
		p = g.adjlist[j].FirstEdge;
		/*p为第0个结点的头指针域,指向下一个邻接表结点(由你的输入顺序决定)*/
		while (p) {
		/*p不为空*/
			if (visited[p->adjvex] == 0) {
			/*p的邻接表节点存在且未被访问(标志数组为0)*/
				printf("%c ", g.adjlist[p->adjvex].vertex);
				queue[rear++] = p->adjvex;
				/*p的邻接表结点的序号入队*/
				visited[p->adjvex] = 1;
				/*标记已被访问*/
			}
			p = p->next;
		}
	}
}

int BfsTraverse(LinkedGraph g) {
	int i, count = 0;
	for (i = 0; i < g.n; i++) {
		visited[i] = 0;                //初始化标志数组
	}
	for (i = 0; i < g.n; i++) {
		if (!visited[i]) {              //未访问过
			count++;                 //连通分量个数加一
			bfs(g, i);
		}
		return(count);
	}
}

int main() {
	LinkedGraph g;
	int count;
	create(&g, 0);
	printf("\nDFS:\n");
	DfsTraverse(g);
	printf("BFS:\n");
	count = BfsTraverse(g);
	printf("\n该图共有%d个连通分量\n", count);
	return 0;
}

接下来我们再用一个有向图举个例子:
在这里插入图片描述

程序运行结果:
在这里插入图片描述

发布了20 篇原创文章 · 获赞 10 · 访问量 1227

猜你喜欢

转载自blog.csdn.net/qq_43613793/article/details/103194131