数据结构:图的遍历--深度优先、广度优先

图的遍历:深度优先、广度优先

遍历

图的遍历是指从图中的某一顶点出发,按照一定的策略访问图中的每一个顶点。当然,每个顶点有且只能被访问一次。

在图的遍历中,深度优先和广度优先是最常使用的两种遍历方式。这两种遍历方式对无向图和有向图都是适用的,并且都是从指定的顶点开始遍历的。先看下两种遍历方式的遍历规则:

深度优先

深度优先遍历也叫深度优先搜索(Depth First Search)。它的遍历规则:不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。

具体点,给定一图G=<V,E>,用visited[i]表示顶点i的访问情况,则初始情况下所有的visited[i]都为false。假设从顶点V0开始遍历,则下一个遍历的顶点是V0的第一个邻接点Vi,接着遍历Vi的第一个邻接点Vj,……直到所有的顶点都被访问过。

所谓的第一个是指在某种存储结构中(邻接矩阵邻接表),所有邻接点中存储位置最近的,通常指的是下标最小的。在遍历的过程中有两种情况经常出现

  1. 某个顶点的邻接点都已被访问过的情况,此时需回溯已访问过的顶点。
  2. 图不连通,所有的已访问过的顶点都已回溯完了,仍找不出未被访问的顶点。此时需从下标0开始检测visited[i],找到未被访问的顶点i,从i开始新一轮的深度搜索。

看一个例子

从V0开始遍历

遍历分析:V0有两个邻接点V1和V2,选择下标最小的V1遍历。接着从V1开始深度遍历,V1只有邻接点V3,也就是没有选的:遍历V3。接着从V3开始遍历,V3只有邻接点V0,而V0已经被遍历过。此时出现了上面提到的情况一,开始回溯V1,V1无未被遍历的邻接点,接着回溯V0,V0有一个未被遍历的邻接点V2,新的一轮深度遍历从V2开始。V2无邻接点,且无法回溯。此时出现了情况二,检测visited[i],只有V4了。深度遍历完成。看到回溯,应该可以想到需要使用栈。

遍历序列是

V0->V1->V3->V2->V4。

从其它顶点出发的深度遍历序列是:

V1->V3->V0->V2->V4。

V2->V0->V1->V3->V4。

V3->V0->V1->V2->V4。

V4->V2->V0->V1->V3。

以上结果,我们稍后用于测试程序。

结合在图的实现:邻接矩阵中的代码,我们看下在邻接矩阵形式下的图的深度遍历算法:

深度优先代码

/*
深度优先搜索
从vertex开始遍历,visit是遍历顶点的函数指针
*/
void Graph::dfs(int vertex, void (*visit)(int))
{
	stack<int> s;
	//visited[i]用于标记顶点i是否被访问过
	bool *visited = new bool[numV];
	//count用于统计已遍历过的顶点数
	int i, count;
	for (i = 0; i < numV; i++)
		visited[i] = false;
	count = 0;
	while (count < numV)
	{
		visit(vertex);
		visited[vertex] = true;
		s.push(vertex);
		count++;
		if (count == numV)
			break;
		while (visited[vertex])
		{
			for (i = 0; i < numV
				&& (visited[i]
				|| matrix[vertex][i] == 0 || matrix[vertex][i] == MAXWEIGHT); i++);
			if (i == numV)  //当前顶点vertex的所有邻接点都已访问完了
			{
				if (!s.empty())
				{
					s.pop();   //此时vertex正是栈顶,应先出栈
					if (!s.empty())
					{
						vertex = s.top();
						s.pop();
					}
					else  //若栈已空,则需从头开始寻找新的、未访问过的顶点
					{
						for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
					}
				}
				else  //若栈已空,则需从头开始寻找新的、未访问过的顶点
				{
					for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
				}
			}
			else  //找到新的顶点应更新当前访问的顶点vertex
				vertex = i;
		}
	}
	delete[]visited;
}

其它代码前面已经见过,就不给出了,下面看下图的广度遍历。深度遍历和广度遍历的测试,稍后一并给出。

广度优先

广度优先遍历也叫广度优先搜索(Breadth First Search)。它的遍历规则:

  1. 先访问完当前顶点的所有邻接点。(应该看得出广度的意思)
  2. 先访问顶点的邻接点先于后访问顶点的邻接点被访问。

具体点,给定一图G=<V,E>,用visited[i]表示顶点i的访问情况,则初始情况下所有的visited[i]都为false。假设从顶点V0开始遍历,且顶点V0的邻接点下表从小到大有Vi、Vj...Vk。按规则1,接着应遍历Vi、Vj和Vk。再按规则2,接下来应遍历Vi的所有邻接点,之后是Vj的所有邻接点,...,最后是Vk的所有邻接点。接下来就是递归的过程...

在广度遍历的过程中,会出现图不连通的情况,此时也需按上述情况二来进行:测试visited[i]...。在上述过程中,可以看出需要用到队列。

举个例子,还是同样一幅图:

从V0开始遍历

遍历分析:V0有两个邻接点V1和V2,于是按序遍历V1、V2。V1先于V2被访问,于是V1的邻接点应先于V2的邻接点被访问,那就是接着访问V3。V2无邻接点,只能看V3的邻接点了,而V0已被访问过了。此时需检测visited[i],只有V4了。广度遍历完毕。

遍历序列是

V0->V1->V2->V3->V4。

从其它顶点出发的广度遍历序列是

V1->V3->V0->V2->V4。

V2->V0->V1->V3->V4。

V3->V0->V1->V2->V4。

V4->V2->V0->V1->V3。

以上结果,我们同样用于测试程序。

在邻接矩阵下,图的广度遍历算法

广度优先代码

/*
广度优先搜索
从vertex开始遍历,visit是遍历顶点的函数指针
*/
void Graph::bfs(int vertex, void(*visit)(int))
{
	//使用队列
	queue<int> q;
	//visited[i]用于标记顶点i是否被访问过
	bool *visited = new bool[numV];
	//count用于统计已遍历过的顶点数
	int i, count;
	for (i = 0; i < numV; i++)
		visited[i] = false;
	q.push(vertex);
	visit(vertex);
	visited[vertex] = true;
	count = 1;
	while (count < numV)
	{
		if (!q.empty())
		{
			vertex = q.front();
			q.pop();
		}
		else
		{
			for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
			visit(vertex);
			visited[vertex] = true;
			count++;
			if (count == numV)
				return;
			q.push(vertex);
		}
		//代码走到这里,vertex是已经访问过的顶点
		for (int i = 0; i < numV; i++)
		{
			if (!visited[i] && matrix[vertex][i] > 0 && matrix[vertex][i] < MAXWEIGHT)
			{
				visit(i);
				visited[i] = true;
				count ++;
				if (count == numV)
					return;
				q.push(i);
			}
		}
	}
	delete[]visited;
}

结合两种遍历的代码,我们对同一幅图进行测试,它的主函数是

void visit(int vertex)
{
	cout << setw(4) << vertex;
}
int main()
{
	cout << "******图的遍历:深度优先、广度优先***by David***" << endl;
	bool isDirected, isWeighted;
	int numV;
	cout << "建图" << endl;
	cout << "输入顶点数 ";
	cin >> numV;
	cout << "边是否带权值,0(不带) or 1(带) ";
	cin >> isWeighted;
	cout << "是否是有向图,0(无向) or 1(有向) ";
	cin >> isDirected;
	Graph graph(numV, isWeighted, isDirected);
	cout << "这是一个";
	isDirected ? cout << "有向、" : cout << "无向、";
	isWeighted ? cout << "有权图" << endl : cout << "无权图" << endl;
	graph.createGraph();
	cout << "打印邻接矩阵" << endl;
	graph.printAdjacentMatrix();
	cout << endl;
	cout << "深度遍历" << endl;
	for (int i = 0; i < numV; i++)
	{
		graph.dfs(i, visit);
		cout << endl;
	}
	cout << endl;
	cout << "广度遍历" << endl;
	for (int i = 0; i < numV; i++)
	{
		graph.bfs(i, visit);
		cout << endl;
	}
	system("pause");
	return 0;
}

运行

仔细对照测试结果,我们的代码是没有问题的。

完整代码下载:图的遍历:深度优先、广度优先

小结

对于某个图来说,深度优先遍历和广度优先遍历的序列不是唯一的,但当图的存储结构一确定,它的遍历序列就是唯一的。因为当有多个候选点时,我们总是优先选择下标最小的。

转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/38323633

若有所帮助,顶一个哦!

专栏目录:数据结构与算法目录

数据结构:图的遍历--深度优先、广度优先

时间: 2024-12-30 19:54:03

数据结构:图的遍历--深度优先、广度优先的相关文章

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先 本篇名言:"生活真象这杯浓酒 ,不经三番五次的提炼呵 , 就不会这样一来可口 ! -- 郭小川" 继续看下广度优先的遍历,上篇我们看了深度遍历是每次一个节点的链表是走到底的. 欢迎转载,转载请标明出处:http://write.blog.csdn.net/postedit/47029275 1.  原理 首先,从图的某个顶点v0出发,访问了v0之后,依次访问与v0相邻的未被访问的顶点,然后分别从这些顶点出发,广度优先遍历,直至所有的

C#与数据结构--图的遍历

C#与数据结构--图的遍历 8.2 图的存储结构 图 的存储结构除了要存储图中各个顶点的本身的信息外,同时还要存储顶点与顶点之间的所有关系(边的信息),因此,图的结构比较复杂,很难以数据元素在存储区 中的物理位置来表示元素之间的关系,但也正是由于其任意的特性,故物理表示方法很多.常用的图的存储结构有邻接矩阵.邻接表.十字链表和邻接多重表. 8.2.1  邻接矩阵表示法 对于一个具有n个顶点的图,可以使用n*n的矩阵(二维数组)来表示它们间的邻接关系.图8.10和图8.11中,矩阵A(i,j)=1

浅析数据结构-图的遍历

上一篇了解图的基本概念,包括图的分类.术语以及存储结构.本篇就是应用图的存储结构,将图进行数据抽象化,应用遍历方法,对数据进行遍历.由于图复杂的数据结构,一定保证图中所有顶点被遍历.如果只访问图的顶点而不关注边的信息,那么图的遍历十分简单,使用一个foreach语句遍历存放顶点信息的数组即可.但是,如果为了实现特定算法,就必须要根据边的信息按照一定的顺序进行遍历.图的遍历算法是求解图的连通性问题.拓扑排序和求解关键路径等算法的基础. 一.图的遍历   图的数据结构相对树复杂,图的任一顶点都可能和

数据结构-图的遍历

图的遍历是指从一个顶点出发,访问且仅一次访问图中其余所有顶点,不是所有边的处理.是求图的连通性,拓扑排序,路径求解等问题的基础. 非常基本的图的遍历方法有深度优先搜索法和广度(宽度)优先搜索法. 深度优先搜索,Depth First Search,DFS 深度优先搜索法是树的先根遍历的推广,它的基本思想是:从图G的某个顶点v0出发,访问v0,然后选择一个与v0相邻且没被访问过的顶点vi访问,再从vi出发选择一个与vi相邻且未被访问的顶点vj进行访问,依次继续.如果当前被访问过的顶点的所有邻接顶点

图的遍历---深度优先遍历与广度优先遍历

对下图进行遍历,分别采用深度优先和广度优先 1.深度优先遍历的主要思想:首先从一个未被访问的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点: 当没有未访问过的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有顶点都被访问. 显然,深度优先遍历是沿着图的某一条分支遍历直到末端,然后回溯,再沿着另一条进行同样的遍历,直到所有顶点被访问. /*深度优先搜索算法遍历图的各个顶点*/ #include<stdio.h> int n, sum, book[101]; int e[101][101

【图的遍历】广度优先遍历(DFS)、深度优先遍历(BFS)及其应用

无向图满足约束条件的路径 • 目的:掌握深度优先遍历算法在求解图路径搜索问题的应用 内容:编写一个程序,设计相关算法,从无向图G中找出满足如下条件的所有路径  (1)给定起点u和终点v.  (2)给定一组必经点,即输出的路径必须包含这些点.  (3)给定一组必避点,即输出的路径必须不能包含这些点. 来源:<数据结构教程(第五版)>李春葆著,图实验11. 原文地址:https://www.cnblogs.com/sunbines/p/9028903.html

数据结构(三十二)图的遍历之广度优先遍历

一.广度优先遍历算法描述 广度优先遍历(Breadth_First_Search),又称为广度优先搜索,简称BFS.图的广度优先遍历类似于树的层序遍历. BFS算法描述:从图中的某个顶点v开始,先访问该顶点,再依次访问该顶点的每一个未被访问过的邻接点w1,w2,...:然后按此顺序访问顶点w1,w2...的各个还未 被访问过的邻接点.重复上述过程,直到图中的所有顶点都被访问过为止. 以下图为例子,顶点访问序列为{A B F C I G E D H} 二.广度优先遍历算法实现 原文地址:https

图的遍历之广度优先搜索(Breadth First Search)

描述 广度优先搜索算法(Breadth First Search)与树的层序遍历(level-order traversal)类似,基本思想是思想是: 从图中某顶点v出发,访问v之后,并将其访问标志置为已被访问,即visited[i]=1: 依次访问v的各个未曾访问过的邻接点: 分别从这些邻接点出发依次访问它们的邻接点,并使得"先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到: 如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为

数据结构-图的遍历之Bellman-Ford算法和SPFA算法

一.Bellman-Ford算法 用于解决单源最短路径的问题,但也能够处理有负权边的情况.这是与Djikstra算法不同的地方. 关于复杂度,要比Djikstra的复杂度更高一点.O(VE),而Djikstra复杂度是O(V^2),V是点的数量,E是边的数量 原理,就是会出现负环的情况,会使得最短路径越来越小,进而产生错误:如果出现负环,源点无法到达,那么也是不会影响求解的. 设置d数组,用于存储最短路径的距离,如果存在可以到达的负环,那么返回false:如果不存在,那么数组d中存储的就是最短距