概述
图作为数据结构书中较为复杂的数据结构,对于图的存储方式分邻接矩阵和邻接表两种方式。在这篇博客中,主要讲述邻接矩阵下的图的深度优先遍历(DFS)与广度优先遍历(BFS)。
广度优先遍历(BFS)
BFS 算法的思想是:对一个无向连通图,在访问图中某一起始顶点 v 后,由 v 出发,依次访问 v 的所有未访问过的邻接顶点 w1, w2, w3, …wt;然后再顺序访问 w1, w2, w3, …wt 的所有还未访问过的邻接顶点;再从这些访问过的顶点出发,再访问它们的所有还未访问过的邻接顶点,……,如此直到图中所有顶点都被访问到为止。
算法叙述:
1)首先初始化队列为空
2)把初始结点入队,并把对应访问数组isvisit元素置1,之后依次把其未被访问的邻接点入队,之后打印当前结点
3)用当前结点保存为出队元素,重复2)直至队列为空。
//广度优先遍历BFS
void BFS(int vertex){
queue<int> queue;
queue.push(vertex); //初始结点入队
this->isvisited[vertex] = 1;
int now;
while(!queue.empty()){ //队不空一直循环
now = queue.front(); //对头元素出栈
queue.pop();
cout<<now<<" "; //打印当前结点
int i = 1;
while(i <= this->Nv){ //与当前结点相邻且未被访问的结点入队
if(this->G[now][i] == 1 && isvisited[i] == 0){
queue.push(i);
this->isvisited[i] = 1; //访问数组相应的置1
}
i++;
}
}
}
深度优先遍历(DFS)——递归版本
递归算法:
1)访问起点v0
2)依次以v0的未访问的连接点为起点,DFS搜索图,直至图中所有与v0路径相通的顶点都被访问。
3)若该图为非连通图,则图中一定还存在未被访问的顶点,选取该顶点为起点,重复上述DFS过程,直至图中全部顶点均被访问过为止。
//递归深度优先遍历DFS
void DFS1(int vertex){
cout<<vertex<<" "; //打印第一个结点
this->isvisited[vertex] = 1; //相应位的访问数组置1
for(int i = 1 ; i <= this->Nv ; i++){
//依次递归遍历当前结点的未被访问的邻接点
if(this->G[vertex][i] == 1 && this->isvisited[i] == 0){
this->isvisited[i] = 0;
this->DFS1(i);
this->isvisited[i] = 0;
}
}
this->isvisited[vertex] = 0;
}
深度优先遍历(DFS)——非递归版本
非递归算法:
1)首先初始化待使用栈,然后将第一个结点入栈
2)然后只要栈不空,重复下面的操作:将栈顶元素弹出,然后看该元素是否访问过
3)若没访问过,则访问,置访问标记,然后将该元素的所有未被访问的相邻顶点入栈(注意是全部,所以应用一个for或while循环来判断哪些元素该入栈)
4)重复2,直至全部顶点均被访问过。
//非递归深度优先遍历DFS
void DFS2(int vertex){
stack<int> stack;
stack.push(vertex); //当前结点入栈
this->isvisited[vertex] = 1; //相应位的访问数组置1
int now;
while(!stack.empty()){ //栈不空则一直循环
now = stack.top(); //当前结点出栈
stack.pop();
cout<<now<<" "; //打印当前结点
//把当前结点的未被访问的邻接点依次入栈并把相应访问数组置1
for(int i = 1 ; i <= this->Nv ; i++){
if(this->G[now][i] != 0 && this->isvisited[i] == 0){
stack.push(i);
this->isvisited[i] = 1;
}
}
}
}
例子
下面的程序所基于的图结构够如下:
全部代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;
class Graph{
private:
int** G; //邻接矩阵
int* isvisited; //访问数组
int Nv; //顶点数
int Ne; //边数
public:
//构造函数
Graph(int nv , int ne){
this->Nv = nv;
this->Ne = ne;
this->G = new int*[nv+1];
this->isvisited = new int[nv+1];
memset(isvisited,0,sizeof(isvisited[0])*(nv+1));
for(int i = 0 ; i < nv+1 ; i++){
G[i] = new int[nv+1];
memset(G[i],0,sizeof(G[i][0])*(nv+1));
}
cout<<"请输入边:"<<endl;
for(int i = 0 ; i < ne ; i++){
int a,b;
cin>>a>>b;
this->G[a][b] = 1;
this->G[b][a] = 1;
}
}
//重置函数
void reset(){
memset(this->isvisited,0,sizeof(this->isvisited[0])*(this->Nv+1));
}
//广度优先遍历BFS
void BFS(int vertex){
queue<int> queue;
queue.push(vertex); //初始结点入队
this->isvisited[vertex] = 1;
int now;
while(!queue.empty()){ //队不空一直循环
now = queue.front(); //对头元素出栈
queue.pop();
cout<<now<<" "; //打印当前结点
int i = 1;
while(i <= this->Nv){ //与当前结点相邻且未被访问的结点入队
if(this->G[now][i] == 1 && isvisited[i] == 0){
queue.push(i);
this->isvisited[i] = 1; //访问数组相应的置1
}
i++;
}
}
}
//递归深度优先遍历DFS
void DFS1(int vertex){
cout<<vertex<<" "; //打印第一个结点
this->isvisited[vertex] = 1; //相应位的访问数组置1
for(int i = 1 ; i <= this->Nv ; i++){
//依次递归遍历当前结点的未被访问的邻接点
if(this->G[vertex][i] == 1 && this->isvisited[i] == 0){
this->isvisited[i] = 0;
this->DFS1(i);
this->isvisited[i] = 0;
}
}
this->isvisited[vertex] = 0;
}
//非递归深度优先遍历DFS
void DFS2(int vertex){
stack<int> stack;
stack.push(vertex); //当前结点入栈
this->isvisited[vertex] = 1; //相应位的访问数组置1
int now;
while(!stack.empty()){ //栈不空则一直循环
now = stack.top(); //当前结点出栈
stack.pop();
cout<<now<<" "; //打印当前结点
//把当前结点的未被访问的邻接点依次入栈并把相应访问数组置1
for(int i = 1 ; i <= this->Nv ; i++){
if(this->G[now][i] != 0 && this->isvisited[i] == 0){
stack.push(i);
this->isvisited[i] = 1;
}
}
}
}
};
int main()
{
cout<<"请输入结点总数与边数:"<<endl;
int nv,ne;
cin>>nv>>ne;
Graph graph(nv,ne);
cout<<"请输入第一个访问的结点:"<<endl;
int now;
cin>>now;
cout<<"广度优先遍历(BFS)序列为:"<<endl;
graph.BFS(now);
cout<<endl;
graph.reset();
cout<<"递归深度优先遍历(DFS)序列为:"<<endl;
graph.DFS1(now);
cout<<endl;
graph.reset();
cout<<"非递归深度优先遍历(DFS)序列为:"<<endl;
graph.DFS2(now);
cout<<endl;
return 0;
}