上一节介绍了深度优先搜索,本节将基于上一节介绍的基本数据结构和方法来介绍宽度优先搜索,宽度优先基本原理如下:
宽度优先不太适合递归, 这里利用队列queue来实现宽度优先搜素,queue只能在队尾插入队首删除,该特性用来保证宽度优先搜索。先给出宽度优先的函数实现,这里用邻接表表示图结构,每个节点通过结构体VertexNode(更具体可参考上一节)表示,代码如下:
vector<int> graphBfs(vector<vector<VertexNode> >& graph, const int& start) {
vector<bool> discovered(graph.size(), false);
// 记录各个顶点是否被发现
queue<int> q;
//记录顶点按照宽度优先的访问顺序
vector<int> order;
discovered[start] = true;
q.push(start);
while(!q.empty()) {
int here = q.front();
//每次拿到最顶端的顶点,即队列queue最前面的元素
q.pop();
order.push_back(here);
for (int i = 0; i < graph[here].size(); ++i) {
int there = graph[here][i].index;
if (!discovered[there]) { //如果还没被发现
q.push(there);
discovered[there] = true;
}
}
}
return order;
}
可以看到,深度优先没有通过递归实现,而是走:节点->弹出存储在顺序队列的顶点->发现与弹出节点相连的节点这样的一个循环,从而迭代实现。下面给出完整的代码,图结构读写方法与上一节一样,图结构存储文件:
s a
a s b g
g a h i
i g
h g
b a c d e
e b f
f e
d b
c b
完整代码:
#include<iostream>
#include<vector>
#include<fstream>
#include<sstream>
#include<string>
#include<queue>
#include<unordered_map>
using namespace std;
typedef struct Vertex{
string name;
int index;
Vertex(string inputName, int inputIndex):name(inputName),index(inputIndex){}
Vertex():name(""),index(0){}
} VertexNode;
vector<int> graphBfs(vector<vector<VertexNode> >& graph, const int& start) {
vector<bool> discovered(graph.size(), false);
// 记录各个顶点是否被发现
queue<int> q;
//记录顶点按照宽度优先的访问顺序
vector<int> order;
discovered[start] = true;
q.push(start);
while(!q.empty()) {
int here = q.front();
//每次拿到最顶端的顶点,即队列queue最前面的元素
q.pop();
order.push_back(here);
for (int i = 0; i < graph[here].size(); ++i) {
int there = graph[here][i].index;
if (!discovered[there]) { //如果还没被发现
q.push(there);
discovered[there] = true;
}
}
}
return order;
}
int main() {
unordered_map<string ,int> graphMap; // 图节点名和编号的Map
vector<vector<VertexNode> > adjGraph; // 图的连接表表示法
ifstream graphRdFile("graph_struct.txt");
if(!graphRdFile.good()) {
cout << "open graph file failed!" << endl;
return -1;
}
string line;
int index = 0;
string vertexName;
// 首先对Vertex Name进行编码
while(graphRdFile >> vertexName) {
if (graphMap.find(vertexName) == graphMap.end()) {
graphMap.insert(make_pair(vertexName, index++));
}
}
// 编码与Vertex的反映射
vector<string> indexName = vector<string>(graphMap.size(),"");
for(auto itr=graphMap.begin();itr!=graphMap.end();itr++) {
indexName[itr->second] = itr->first;
}
// 重新读
graphRdFile.clear();
graphRdFile.seekg(0,std::ios::beg);
adjGraph.resize(graphMap.size());
int currentIndex = 0; // 当前图节点的编号
while (getline(graphRdFile, line)) { //按行读,每一行是一个图节点的连接情况
istringstream ss(line);
string tmp;
bool firstFlag = true;
while(ss >> tmp) {
if (firstFlag) {
if (graphMap.find(tmp) != graphMap.end()) {
currentIndex = graphMap[tmp];
} else {
break;
}
firstFlag = false;
continue;
}
if (graphMap.find(tmp) != graphMap.end()) {
adjGraph[currentIndex].emplace_back(VertexNode(tmp,graphMap[tmp]));
}
}
}
// 深度优先搜索测试
vector<bool> visited = vector<bool>(graphMap.size(), false);
vector<int> order = graphBfs(adjGraph, 0);
for(int i = 0; i < order.size(); ++i) {
cout << indexName[order[i]] << endl;
}
return 0;
}
执行结果如下:
s
a
b
g
c
d
e
h
i
f