图的深度优先遍历和广度优先遍历:
从本质上来说都是用来访问图中的所有结点的,只是两种访问的方式不同,深度优先遍历用的是堆栈,先进的后出,而广度优先遍历用的是队列的思想。
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相继入栈并输出![在这里插入图片描述](https://img-blog.csdnimg.cn/20191230124202493.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDI0NDE1NA==,size_16,color_FFFFFF,t_70)
最后依次出栈,深度优先就遍历完了!
从顶点v出发图的深度优先遍历算法的伪代码:
- 访问顶点v; visited[v] = 1;
- w =顶点v的第一个邻接点;
- 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
西街 公寓楼 鸿远楼 修远楼 明远楼 逸夫图书馆 行政楼 南门 树慧园 天行健 澡堂 北门 操场 仙女楼
校医院 东门
运行结果如下: