数据结构——详解图的深度优先遍历和广度优先遍历及代码实现

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

从本质上来说都是用来访问图中的所有结点的,只是两种访问的方式不同,深度优先遍历用的是堆栈,先进的后出,而广度优先遍历用的是队列的思想。

1.图的深度优先遍历

1.1基本思想:

⑴ 访问顶点v;
⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。

例如我们需要用深度优先遍历下面这个图:

首先,我们访问V1结点,V1结点入栈并输出,然后发现V1与V2相连,因此我们将V2入栈,同理,V4和V5也入栈输出,因此遍历序列为V1 V2 V4 V5

在这里插入图片描述

接下来,V5入栈输出后,我们发现与V5相连的两个结点都已经访问过了,因此V5直接出栈,返回到了上一层,即V4,在V4结点处我们发现还有一个结点V8没有被访问到,因此将V8入栈并输出

在这里插入图片描述

同理,V8入栈后会直接出栈,因为与V8相邻的结点都已经被访问过了,接下来V4、V2也出栈,只留下了V1在堆栈中

在这里插入图片描述

这时我们会发现,哎哟,V1还有结点没有访问到哎!哈哈,所以V3、V6、V7相继入栈并输出在这里插入图片描述
最后依次出栈,深度优先就遍历完了!

从顶点v出发图的深度优先遍历算法的伪代码:

  1. 访问顶点v; visited[v] = 1;
  2. w =顶点v的第一个邻接点;
  3. while (w存在)
    3.1 if (w未被访问) 从顶点w出发递归执行该算法;
    3.2 w = 顶点v的下一个邻接点;

1.2深度优先遍历代码实现

在完成深度优先遍历之前,我们首先需要对图进行初始化工作,这儿的图是从文件中直接读取的

template<class DataType>
class MGraph
{
public:
    MGraph();
    ~MGraph(){}
    void DFSTraverse(int v);
    void BFSTraverse(int v);
	int vertexNum,arcNum;//图的顶点数和边数
	DataType vertex[MaxSize];//顶点
private:
    int arc[MaxSize][MaxSize];//图的边长
	bool DFSvisited[MaxSize];//定义一个DFS遍历过标记
	bool BFSvisited[MaxSize];//定义一个BFS遍历过标记
};
template<class DataType>//构造函数
MGraph<DataType>::MGraph()
{
	int i,j;
	fstream fcin_vertex;
	fcin_vertex.open("vertex.txt",ios::in);//打开一个文件用于从文件输入顶点
	vertexNum=0,arcNum=0;//令顶点数、边数赋值为0
    while(!fcin_vertex.eof())
		fcin_vertex>>vertex[vertexNum++];
	fcin_vertex.close();
	memset(DFSvisited,0,sizeof(DFSvisited));//这个函数用来给DFSvisited数组所有成员赋0
	memset(BFSvisited,0,sizeof(BFSvisited));
    for(i=0;i<vertexNum;i++)
        for(j=0;j<vertexNum;j++)
		{
			if(i==j)
				arc[i][j]=0;//如果是相同顶点,则初始化为0
			else
				arc[i][j]=MAX;//否则将边长初始化为MAX;
		}
	fstream fcin_arc;
	fcin_arc.open("arc.txt",ios::in);//打开一个文件用于从文件输入边
	i=0,j=0;
	while(i!=-1)
	{
		fcin_arc>>i;
		if(i==-1)
			continue;
		else
		{
			fcin_arc>>j;
			if(arc[i][j]==0||arc[i][j]==MAX)//如果这条边等于0或者等于MAX,说明这条边还没被修改过,这时候修改一次,边数+1,
				                            //否则,则说明这条边已经被修改过,再修改边数也不会增加
				arcNum++;
			fcin_arc>>arc[i][j];
			arc[j][i]=arc[i][j];//由于是无向图,i到j的距离等于j到i的距离
		}
	}
	fcin_arc.close();
}

接下来是深度优先遍历算法实现:

template<class DataType>
void MGraph<DataType>::DFSTraverse(int v)
{
	int j;
    cout<<vertex[v]<<" ";//遍历到该点,即输出
	DFSvisited[v]=1;//然后将该点的遍历标记置1
    for(j=0;j<vertexNum;j++)
        if(arc[v][j]!=0&&arc[v][j]!=MAX&&DFSvisited[j]==0)//如果边不等于0并且不等于MAX并且该点没有遍历过,就深度优先遍历
			DFSTraverse(j);
}
深度优先遍历也用到了递归的思想,如果边不等于0并且不等于MAX并且该点没有遍历过,就深度优先遍历,再次调用这个函数即可。

2.图的广度优先遍历

2.1基本思想:

⑴ 访问顶点v;
⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;
⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。

例如还是刚才那个图,我们用广度优先遍历一遍:

在最开始时,V1入列,然后出列输出

在这里插入图片描述

紧接着,与V1相连的结点有V2和V3,因此V2和V3入列并输出

在这里插入图片描述

接下来V2出列,与V2相连的结点有V4和V5,所以V4和V5入列并输出

在这里插入图片描述

接下来同理,V3出列,V6和V7入列并输出

在这里插入图片描述

然后V4出列,与V4相连的还有V8,所以V8入列并输出

在这里插入图片描述

这样就遍历完了,后面的结点由于相邻结点都被访问过了,因此直接出列就完事了

从顶点v出发图的广度优先遍历算法的伪代码:

1. 初始化队列Q;
2. 访问顶点v; visited [v]=1; 顶点v入队列Q;
3. while (队列Q非空) 
    3.1 v=队列Q的队头元素出队;
    3.2 w=顶点v的第一个邻接点;
    3.3 while (w存在) 
          3.3.1 如果w 未被访问,则
                   访问顶点w; visited[w]=1; 顶点w入队列Q;
          3.3.2 w=顶点v的下一个邻接点;


广度优先遍历代码实现:

template<class DataType>//广度优先遍历
void MGraph<DataType>::BFSTraverse(int v)
{
	int Q[MaxSize];
    int front=-1,rear=-1,j;
    cout<<vertex[v]<<" ";//先输出开始广度遍历的结点
	BFSvisited[v]=1;//并把刚才输出的结点标记为1
	Q[++rear]=v;//将该结点入队
    while(front!=rear)//如果队非空
    {
        v=Q[++front];//把队头结点出队,并将出队的结点的数组下标赋给v
        for(j=0;j<vertexNum;j++)//for循环寻找下一个满足  边不等于0并且不等于MAX并且该点没有遍历过  的结点
			if(arc[v][j]!=0&&arc[v][j]!=MAX&&BFSvisited[j]==0)//如果边不等于0并且不等于MAX并且该点没有遍历过
			{
				cout<<vertex[j]<<" ";//输出找到的结点
				BFSvisited[j]=1;//标记置1
				Q[++rear]=j;//结点入队
			}
    }
}

最后给出完整的两种遍历实现的代码

#include<iostream>
using namespace std;
#include<conio.h>//getch()函数头文件
#include<string>//新版vc的getch()函数头文件
#include<stdlib.h>//调节颜色头文件
#include<fstream>
const int MaxSize=100;
const int MAX=10000;//最大距离
template<class DataType>
class MGraph
{
public:
    MGraph();
    ~MGraph(){}
    void DFSTraverse(int v);
    void BFSTraverse(int v);
	int vertexNum,arcNum;//图的顶点数和边数
	DataType vertex[MaxSize];//顶点
private:
    int arc[MaxSize][MaxSize];//图的边长
	bool DFSvisited[MaxSize];//定义一个DFS遍历过标记
	bool BFSvisited[MaxSize];//定义一个BFS遍历过标记
};
template<class DataType>//构造函数
MGraph<DataType>::MGraph()
{
	int i,j;
	fstream fcin_vertex;
	fcin_vertex.open("vertex.txt",ios::in);//打开一个文件用于从文件输入顶点
	vertexNum=0,arcNum=0;//令顶点数、边数赋值为0
    while(!fcin_vertex.eof())
		fcin_vertex>>vertex[vertexNum++];
	fcin_vertex.close();
	memset(DFSvisited,0,sizeof(DFSvisited));//这个函数用来给DFSvisited数组所有成员赋0
	memset(BFSvisited,0,sizeof(BFSvisited));
    for(i=0;i<vertexNum;i++)
        for(j=0;j<vertexNum;j++)
		{
			if(i==j)
				arc[i][j]=0;//如果是相同顶点,则初始化为0
			else
				arc[i][j]=MAX;//否则将边长初始化为MAX;
		}
	fstream fcin_arc;
	fcin_arc.open("arc.txt",ios::in);//打开一个文件用于从文件输入边
	i=0,j=0;
	while(i!=-1)
	{
		fcin_arc>>i;
		if(i==-1)
			continue;
		else
		{
			fcin_arc>>j;
			if(arc[i][j]==0||arc[i][j]==MAX)//如果这条边等于0或者等于MAX,说明这条边还没被修改过,这时候修改一次,边数+1,
				                            //否则,则说明这条边已经被修改过,再修改边数也不会增加
				arcNum++;
			fcin_arc>>arc[i][j];
			arc[j][i]=arc[i][j];//由于是无向图,i到j的距离等于j到i的距离
		}
	}
	fcin_arc.close();
}
template<class DataType>
void MGraph<DataType>::DFSTraverse(int v)
{
	int j;
    cout<<vertex[v]<<" ";//遍历到该点,即输出
	DFSvisited[v]=1;//然后将该点的遍历标记置1
    for(j=0;j<vertexNum;j++)
        if(arc[v][j]!=0&&arc[v][j]!=MAX&&DFSvisited[j]==0)//如果边不等于0并且不等于MAX并且该点没有遍历过,就深度优先遍历
			DFSTraverse(j);
}
template<class DataType>//广度优先遍历
void MGraph<DataType>::BFSTraverse(int v)
{
	int Q[MaxSize];
    int front=-1,rear=-1,j;
    cout<<vertex[v]<<" ";//先输出开始广度遍历的结点
	BFSvisited[v]=1;//并把刚才输出的结点标记为1
	Q[++rear]=v;//将该结点入队
    while(front!=rear)//如果队非空
    {
        v=Q[++front];//把队头结点出队,并将出队的结点的数组下标赋给v
        for(j=0;j<vertexNum;j++)//for循环寻找下一个满足  边不等于0并且不等于MAX并且该点没有遍历过  的结点
			if(arc[v][j]!=0&&arc[v][j]!=MAX&&BFSvisited[j]==0)//如果边不等于0并且不等于MAX并且该点没有遍历过
			{
				cout<<vertex[j]<<" ";//输出找到的结点
				BFSvisited[j]=1;//标记置1
				Q[++rear]=j;//结点入队
			}
    }
}

int main()
{
	MGraph<string> graph;//构造图
	char N;
	int i,j;
	system("color 0A");
	while(true)
	{
		cout<<"请输入需要的功能N:"<<endl;
		cout<<"0.退出\n";
		cout<<"1.统计图中边数\n";
		cout<<"2.深度优先遍历\n";
		cout<<"3.广度优先遍历\n";
		cin>>N;
		if(N=='0')break;
		switch(N)
		{
		case '1':
			cout<<"图的边数为:"<<graph.arcNum<<endl;
			cout<<"--请按任意键继续--\n";
			_getch();
			system("CLS");
			break;
		case '2':
			cout<<"图的深度优先遍历结果为:";
			graph.DFSTraverse(0);
			cout<<endl;
			cout<<"--请按任意键继续--\n";
			_getch();
			system("CLS");
			break;
		case '3':
			cout<<"图的广度优先遍历结果为:";
			graph.BFSTraverse(0);
			cout<<endl;
			cout<<"--请按任意键继续--\n";
			_getch();
			system("CLS");
			break;
		default:
			cout<<"输入错误,请重新输入!\n";
			cout<<"--请按任意键继续--\n";
			_getch();
			system("CLS");
		}
	}
	return 0;
}

其中需要读取两个txt文件,一个是图的边信息,另一个是图的顶点信息:

1.图的边信息,文件名命名为arc

0	1	50
0	2	80
1	2	100
1	3	200
2	4	200
3	4	100
3	5	150
4	5	70
4	6	70
5	6	100
5	8	300
6	7	50
8	9	100
8	10	70
8	13	50
9	10	70
10	12	50
10	13	150
12	11	150
12	13	50
13	14	300
13	15	100
-1

2.图的顶点信息,文件名命名为vertex

西街	公寓楼	鸿远楼	修远楼	明远楼	逸夫图书馆	行政楼	南门	树慧园	天行健	澡堂	北门	操场	仙女楼
校医院	东门

运行结果如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 47
    点赞
  • 177
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值