一、广度优先遍历的定义
广度优先遍历(Breadth_First_Search),又称为广度优先搜索,简称BFS;如果说深度优先遍历类似树的前序遍历,那么广度优先遍历就类似于树的层序遍历;不过相对于深度优先遍历,广度优先遍历借助了一个队列来辅助,利用队列先进先出的性质实现广度优先,看似复杂一些,其实也很好理解;先看代码~
二、广度优先的实现
该算法的整体思路是先初始化标志数组,然后用双重循环各顶点;第一层循环,防止图不连通;二层循环是广度优先的核心,将顶点i的所有邻接顶点都放入队列,然后对队列进行出队操作,出队时返回出队顶点在顶点表中的位置,然后再将该出队顶点的邻接顶点入队,知道队列为空,这样该连通范围内的所有顶点都被遍历到了;
bool visited[MAXVEX];与深度优先同样的标志数组,去重;
函数OutQueue,为了能够返回出队顶点的位置;
bool visited[MAXVEX];
int OutQueue(Graph &graph, queue<char> &queue) {
char tempVex = queue.front();
for(int i = 0; i < graph.numVertexes; ++i) {
if(tempVex == graph.vexs[i]) {
queue.pop();
return i;
}
}
}
void BFSTraverse(Graph &graph) {
queue<char> queue;
for(int i = 0; i < graph.numVertexes; ++i) {
visited[i] = false;
}
for(int i = 0; i < graph.numVertexes; ++i) {
if(!visited[i]) {
visited[i] = true;
cout << graph.vexs[i] << " ";
queue.push(graph.vexs[i]);
while(!queue.empty()) {
int i = OutQueue(graph, queue);
for(int j = 0; j < graph.numVertexes; ++j) {
if(graph.arc[i][j] && !visited[j]) {
visited[j] = true;
cout << graph.vexs[j] << " ";
queue.push(graph.vexs[j]);
}
}
}
}
}
}
大体上思路是一样的,只不过区别在于对邻接顶点表的遍历方式不一样,邻接表自然用的是链表的遍历方式,关于辅助队列的用法也是一样的;
bool visited[MAXVEX];
int OutQueue(Graph &graph, queue<char> &queue) {
char tempVex = queue.front();
for(int i = 0; i < graph.vexNum; ++i) {
if(tempVex == graph.vexArr[i].vexName) {
queue.pop();
return i;
}
}
}
void BFSTraverse(Graph &graph) {
queue<char> queue;
eNode* p;
for(int i = 0; i < graph.vexNum; ++i) {
visited[i] = false;
}
for(int i = 0; i < graph.vexNum; ++i) {
if(!visited[i]) {
visited[i] = true;
cout << graph.vexArr[i].vexName << " ";
queue.push(graph.vexArr[i].vexName);
while(!queue.empty()) {
int i = OutQueue(graph, queue);
p = graph.vexArr[i].vNext;
while(p) {
if(!visited[p->vexOrderNum]) {
visited[p->vexOrderNum] = true;
cout << graph.vexArr[p->vexOrderNum].vexName << " ";
queue.push(graph.vexArr[p->vexOrderNum].vexName);
}
p = p->next;
}
}
}
}
}
三、测试
#include<iostream>
#include<queue>
using namespace std;
#define MAXVEX 20
#define INFINITY 0 //用于初始化时填充邻接矩阵
typedef struct Graph {
char vexs[MAXVEX];
int arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
}*pGraph;
void CreateGraph(Graph &graph) {
cout << "输入顶点数和边数:";
cin >> graph.numVertexes >> graph.numEdges;
//建立顶点表
for(int i = 0; i < graph.numVertexes; ++i) {
cout << "请输入第" << i + 1 << "个顶点名:";
cin >> graph.vexs[i];
}
//初始化邻接矩阵
for(int i = 0; i < graph.numVertexes; ++i) {
for(int j = 0; j < graph.numVertexes; ++j) {
graph.arc[i][j] = INFINITY;
}
}
//建立邻接矩阵
int x, y, w;
for(int i = 0; i < graph.numEdges; ++i) {
cout << "输入边的下标x,y和权值w:";
cin >> x >> y >> w;
graph.arc[x][y] = w;
graph.arc[y][x] = w;
}
}
bool visited[MAXVEX];
int OutQueue(Graph &graph, queue<char> &queue) {
char tempVex = queue.front();
for(int i = 0; i < graph.numVertexes; ++i) {
if(tempVex == graph.vexs[i]) {
queue.pop();
return i;
}
}
}
void BFSTraverse(Graph &graph) {
queue<char> queue;
for(int i = 0; i < graph.numVertexes; ++i) {
visited[i] = false;
}
for(int i = 0; i < graph.numVertexes; ++i) {
if(!visited[i]) {
visited[i] = true;
cout << graph.vexs[i] << " ";
queue.push(graph.vexs[i]);
while(!queue.empty()) {
int i = OutQueue(graph, queue);
for(int j = 0; j < graph.numVertexes; ++j) {
if(graph.arc[i][j] && !visited[j]) {
visited[j] = true;
cout << graph.vexs[j] << " ";
queue.push(graph.vexs[j]);
}
}
}
}
}
}
int main() {
Graph graph;
CreateGraph(graph);
for(int i = 0; i < graph.numVertexes; ++i) {
cout << "\t" << graph.vexs[i];
}
cout << "\n\n";
for(int i = 0; i < graph.numVertexes; ++i) {
cout << graph.vexs[i] << "\t";
for(int j = 0; j < graph.numVertexes; ++j) {
cout << graph.arc[i][j] << "\t";
}
cout << "\n\n";
}
cout << "广度优先遍历结果:";
BFSTraverse(graph);
getchar();
return 0;
}
四、总结
其实广度优先遍历和深度优先遍历的区别就是,深度优先是类似于树的前序遍历,一直往下走,直到走到底再递归返回,重复往下深入遍历;而广度优先是先将一个顶点所有邻接顶点放入队列,然后按顺序一个一个出队列,出队列的同时也会将该顶点的所有邻接顶点再入队列,通过顶点的相关性来遍历整个图,两种算法的特点还是很明显不同的;
对比两种遍历算法,他们在时间复杂度上是一样的,不过如果图的顶点和边特别多的时候,不能在短时间完成遍历,遍历的目的是为了寻找合适的顶点,那么选择哪种遍历方式就要仔细斟酌了;深度优先更适合目标比较明确,以找到目标为主要的情况,而广度优先更适合在不断扩大遍历范围时找到相对最优解的情况~
来源:CSDN
作者:长安某~
链接:https://blog.csdn.net/weixin_44816732/article/details/104139306