图的遍历是指,从给定图中任意指定的顶点(称为起始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点仅被访问一次。
遍历过程中得到的顶点序列称为图遍历序列。
图的遍历过程中,根据搜索方法的不同,又可以划分为两种搜索策略:
- 深度优先搜索(DFS,Depth First Search)
- 广度优先搜索(BFS,Breadth First Search)
以下面这个有向图为例,假设起始点为顶点A,按照字母顺序制定优先级。

1. 算法思想
BFS:
从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问”,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。
BFS类似于树的层次遍历。
DFS:
假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
DFS类似于树的先序遍历。
2. 图遍历序列
BFS:A → B → C → D → E → F
DFS:A → C → E → D → F → B(注:此序列是按照栈实现的,如果按照递归的方法,序列会有所不同。)
3. 数据结构
BFS:队列(先进先出)
步骤:
1、首先A入队列,

2、A出队列时,A的后继顶点B,C相应进入队列

3、B出队列时,B的后继顶点C,D中未进过队列的D进入队列

4、C出队列时,C的后继顶点D,E中未进过队列的E进入队列

5、D出队列时,D的后继顶点E,F中未进过队列的F进入队列

6、E出队列时,E没有后继顶点,因此没有顶点进入队列

7、F出队列时,F没有后继顶点,因此没有顶点进入队列

8、此时队列为空,遍历结束

DFS:栈(先进后出)
步骤:
1、首先A入栈

2、A出栈时,A的后继顶点B,C相应入栈

3、C出栈时,C的后继顶点D、E相应入栈

4、E出栈时,E没有后继顶点,因此没有顶点入栈

5、D出栈时,D的后继顶点E、F中未进过栈的F入栈

6、F出栈时,F没有后继顶点,因此没有顶点入栈

7、B出栈时,B的后继顶点C、D均已进过栈,因此没有顶点入栈

8、此时栈为空,遍历结束

4. 代码(C++)
#include <iostream>
#include "vector"
#include <fstream>
#include "queue"
#include "stack"
using namespace std;
//定义结构体
struct Node {
vector<char> N_O;//存储该顶点的后继顶点
bool visited_bfs;//标记bfs过程中该顶点是否已访问
bool visited_dfs;//标记dfs过程中该顶点是否已访问
};
vector<Node> nodes;
//读取图并计算读取时间
void read_graph(const char *filename) {
ifstream file;
file.open(filename, ios::in);
if (!file.is_open()) {
cout << "Failed to read graph file!" << endl;
return;
}
int n, e;
string text_n, text_e;
file >> text_n >> n >> text_e >> e;
nodes.resize(n);
while (file.peek()!=EOF) {
char u, v;
file >> u >> v;
int int_u=u-'A';
nodes[int_u].N_O.push_back(v); //存储u的后继顶点
}
file.close();
}
void bfs(char start){
queue<char> q;//定义一个空队列
q.push(start);//起始点入队
nodes[start-'A'].visited_bfs= true;//将起始点的标志位置为true,即已访问
cout<<"BFS: "<<endl;
while (q.size()>0){//当队列为空时,遍历结束
char vertex=q.front();//q.front()返回队首元素的值,但不删除该元素
int n=vertex-'A';
q.pop();//q.pop()删除队列首元素但不返回其值
for (int i = 0; i < nodes[n].N_O.size(); i++) {
int o=nodes[n].N_O[i]-'A';
//当该顶点的后继顶点未被访问时,将后继顶点入队,并修改其标志位
if(!nodes[o].visited_bfs){
q.push(nodes[n].N_O[i]);
nodes[o].visited_bfs= true;
}
}
cout<<vertex<<" ";
}
cout<<endl;
}
void dfs(char start){
stack<char> s;//定义一个空栈
s.push(start);//起始点入栈
nodes[start-'A'].visited_dfs= true;//将起始点的标志位置为true,即已访问
cout<<"DFS: "<<endl;
while (s.size()>0){//当队列为空时,遍历结束
char vertex=s.top();//s.top()返回栈顶元素, 但不删除该元素
int n=vertex-'A';
s.pop();//s.pop()弹出栈顶元素, 但不返回其值
for (int i = 0; i < nodes[n].N_O.size(); i++) {
int o=nodes[n].N_O[i]-'A';
//当该顶点的后继顶点未被访问时,将后继顶点入栈,并修改其标志位
if(!nodes[o].visited_dfs){
s.push(nodes[n].N_O[i]);
nodes[o].visited_dfs= true;
}
}
cout<<vertex<<" ";
}
cout<<endl;
}
int main(int argc, char* argv[]) {
read_graph(argv[1]);
//初始化,将所有顶点的标志位都置为false,即未被访问
for (int i = 0; i < nodes.size(); i++) {
nodes[i].visited_bfs= false;
nodes[i].visited_dfs= false;
}
bfs('A');
cout<<endl;
dfs('A');
return 0;
}
运行结果如下图所示:
其中使用的examp.txt文件的内容如下图所示:

参考链接:
https://www.jianshu.com/p/0a597adfc1e8
https://blog.csdn.net/yue_2018/article/details/89060556
本文介绍了图的遍历方法,包括深度优先搜索(DFS)和广度优先搜索(BFS),并以一个有向图为例,展示了从顶点A开始的遍历序列。BFS使用队列实现,DFS使用栈实现,两者遍历顺序不同。此外,还提供了C++代码示例来实现这两种遍历策略。

3779

被折叠的 条评论
为什么被折叠?



