前言
在半年之前写过一篇关于无向图的构建以及相关常用算法的实现 → 图的构建以及BFS(广度优先搜索)、DFS(深度优先搜索)、普里姆最小生成树算法、并查集与kruskal算法
,近日对这些算法进行了一个回顾,并且对其中的代码进行了全面的优化。
无向图构建
还是以之前的无向城市图为例:
上图列出了城市间的连通关系以及城市间的距离信息,下面的算法将围绕这张图进行展开。
使用临接矩阵表示无向图
在计算机中可以通过使用一些比较精妙的方式例如临界表或者临接矩阵来表示图中各节点的连接关系。笔者更加偏向于使用 临接矩阵。
体现代码中是通过我们手动传入一个装有城市间信息的数组,然后将这些信息体现到矩阵中。
Relation of Citys | 邵阳 | 长沙 | 衡阳 | 深圳 | 大庆 | 岳阳 |
---|---|---|---|---|---|---|
邵阳 | 0 | 235 | 141 | 697 | INF | 364 |
长沙 | 235 | 0 | 190 | 782 | 2682 | 168 |
衡阳 | 141 | 190 | 0 | 641 | INF | INF |
深圳 | 697 | 190 | 641 | 0 | 3356 | INF |
大庆 | INF | 2682 | INF | 3356 | 0 | 2540 |
岳阳 | 364 | 168 | INF | INF | 2540 | 0 |
注: 数字表示城市间的距离,单位km;INF 为 infinite 的缩写,表示城市间并不直接相连。
class Graph {
public:
int cityNum;
vector<string> cityName;//存储所有的城市名
unordered_map<string, int> cityIndex;//kv: 城市名对应的编号
vector<vector<int>> cityInf;//城市间关系矩阵
};
以上为创建图会使用到的变量以及容器。
cityNum : 记录城市数量
cityName : 存储所有的城市名
cityIndex : 通过城市名查找对应的城市索引
cityInf : 临接矩阵
void Graph::createGraph(vector<string>& citys, vector<cityRel*>& cityRelation) {
cityNum = citys.size();
for (auto& city : citys)//数组拷贝
cityName.push_back(city);
/*将cityInf空间设置为 cityNum行,列同行*/
cityInf.resize(cityNum);
for (auto& city : cityInf)
city.resize(cityNum, INFINITE);//默认设置为不可到达
//对每一个城市进行编号,存储到哈希表中
int index = 0;
for (string& city : citys) {
cityIndex[city] = index;
++index;
}
//城市间的信息转换到临界矩阵中
for (auto& city : cityRelation) {
//起始城市索引
int startIndex = cityIndex[city->startCity];
//终止城市索引
int endIndex = cityIndex[city->endCity];
//两城市距离
int distance = city->distance;
//由于是无向图,所以两个方向都需要添加上
cityInf[startIndex][endIndex] = distance;
cityInf[endIndex][startIndex] = distance;
}
//到自身的距离设置为 0
for (int i = 0; i < cityInf.size(); ++i)
cityInf[i][i] = 0;
cout << "城市信息如下:\n\n";
//输出临界矩阵
for (auto& city : citys) {
cout << "\t" << city;
}cout << endl;
for (int i = 0; i < cityInf.size(); ++i) {
cout << citys[i] << " ";
for (int j = 0; j < cityInf[i].size(); ++j) {
if (cityInf[i][j] == INFINITE)//无穷大
cout << "\t∞";
else
cout << "\t" << cityInf[i][j];
}cout << endl;
}
cout << "\n注: 图中数字表示距离,单位: km,其中'∞'表示不可到达" << endl;
}
创建后的临接矩阵如下:
∞ 使用一个宏定义来表示:
#define INFINITE 99999999
图的遍历
BFS广度优先
广度以及深度搜索的代码非常相似,只是使用的数据结构有一丢丢差别。直接放上代码
void Graph::BFS(string startCity) {
queue<int> que;
que.push(cityIndex[startCity]);
//记录城市的访问情况: 避免重复加入
vector<bool> visited(cityNum, false);
vector<int> path;
while (que.empty() == 0) {
int cur = que.front();
que.pop();
path.push_back(cur);
visited[cur] = true;
for (int next = 0; next < cityInf.size(); ++next) {
//能够到达且没被加入过
if (visited[next] == false && cityInf[cur][next] > 0 && cityInf[cur][next] != INFINITE) {
que.push(next);
visited[next] = true;//表示已经访问
}
}
}
cout << "\n广度优先搜索\n\t" << endl;
for (int i = 0; i < cityNum - 1; ++i) {
cout << cityName[path[i]] << " -> ";
}
cout << cityName[path[cityNum - 1]] << endl;
}
DFS深度优先
使用STL库中的stack容器实现:
void Graph::DFS(string startCity) {
stack<int> stk;
stk.push(cityIndex[startCity]);
//记录城市的访问情况: 避免重复加入
vector<bool> visited(cityNum, false);
vector<int> path;
while (stk.empty() == 0) {
int cur = stk.top();
stk.pop();
path.push_back(cur);
visited[cur] = true;
for (int next = 0; next < cityInf.size(); ++next) {
//能够到达且没被加入过
if (visited[next] == false && cityInf[cur][next] > 0 && cityInf[cur][next] != INFINITE) {
stk.push(next);
visited[next] = true;//表示已经访问
}
}
}
cout << "\n深度优先搜索\n\t" << endl;
for (int i = 0; i < cityNum - 1; ++i) {
cout << cityName[path[i]] << " -> ";
}
cout << cityName[path[cityNum - 1]] << endl;
}
递归实现:
void Graph::recurDFS(string startCity, vector<bool>& visited, vector<int>& path) {
if (path.size() >= cityName.size() - 1) {
//注意,这里在装满所有城市之后不会再进入递归
//此时就差最后一个城市未装入,startCity即为该城市
for (int i = 0; i < cityNum - 1; ++i) {
cout << cityName[path[i]] << " -> ";
}
//将最后一个城市输出即可
cout << cityName[cityIndex[startCity]] << endl;
return;
}
int cur = cityIndex[startCity];
visited[cur] = true;
path.push_back(cur);
for (int next = 0; next < cityNum; ++next) {
if (visited[next] == false && cityInf[cur][next] > 0 && cityInf[cur][next] != INFINITE) {
visited[next] = true;
recurDFS(cityName[next], visited, path);
}
}
}
最优二叉生成树
Prim算法
struct cmp {
bool operator()(const Edge* a, const Edge* b) {
return a->distance > b->distance;
}
};
void Graph::primAlgorithm(string startCity) {
int num = cityNum - 1;
int cur = cityIndex[startCity];
vector<bool> visited(cityNum, false);
priority_queue<Edge