整理一些有关图论算法的东西,整理得比较随意,而且也是先整理一些,之后想到啥就补充一点改一点,所以在标题上写个杂。这篇笔记纯粹是整理着自己玩,所以 C++ 代码写得也很随意。
邻接表、拓扑排序
先来看一个有向图无环图。
该有向图的全体边为:
邻接表的结构也一目了然:
写点代码,输入边,输出邻接表,写得可以说是很随意了:
#ifndef GRAPH_GRAPH_H
#define GRAPH_GRAPH_H
#include <unordered_map>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
class graph {
public:
void insert(string, string);//输入
void print();//输出邻接表
private:
unordered_map<int,vector<int>> adjacencylist;//邻接表
vector<int> Indegree;//入度数组
unordered_map<string,int> node2index;//值到内部编号的哈希表
unordered_map<int,string> index2node;//内部编号到值的哈希表
int node_num = 0;//节点数
};
void graph::insert(string s1,string s2){
if(node2index.find(s1) == node2index.end()){
node2index[s1] = node_num;
index2node[node_num] = s1;
++ node_num;
Indegree.push_back(0);
}
if(node2index.find(s2) == node2index.end()){
node2index[s2] = node_num;
index2node[node_num] = s2;
++ node_num;
Indegree.push_back(0);
}
adjacencylist[node2index[s1]].push_back(node2index[s2]);
++Indegree[node2index[s2]];
}
void graph::print() {
for(int i = 0; i < node_num; ++i){
cout << index2node[i]<<' ';
for(int j = 0; j < adjacencylist[i].size(); ++j){
cout<< index2node[adjacencylist[i][j]]<<' ';
}
cout<<endl;
}
}
#endif //GRAPH_GRAPH_H
拓扑排序就是把有向无环图的节点排成一个序列,注意这个“有向”,点的顺序不能反了,如果有边(A,B),那么在序列中A一定在B前面。
贴代码,重复的就不贴了
#ifndef GRAPH_GRAPH_H
#define GRAPH_GRAPH_H
#include <unordered_map>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class graph {
public:
...//同上
vector<string> topsort();
private:
...//同上
int find_zero_indegree();//找入度为0的点,就是找入口呗
};
...//同上
int graph::find_zero_indegree() {
auto iter = find(indegree.begin(), indegree.end(), 0);
if (iter == indegree.end())
return -1;
else {
return iter - indegree.begin();
}
}
vector<string> graph::topsort() {
vector<string> topsort_result;
for (int i = 0; i < node_num; ++i) {
int innode = find_zero_indegree();
if (innode == -1) {
cout << "有环";//还没输出全部的点就找不到入度为0的点了,说明有环
break;
}
indegree[innode] = -1;//删去这个点,直接入度为-1就行了
topsort_result.push_back(index2node[innode]);
for (auto outnode : adjacencylist[innode]) {
--indegree[outnode];
}
}
return topsort_result;
}
#endif //GRAPH_GRAPH_H
输入上面的例子看结果:
有环的话会怎么样呢?当然是输出有环啦,不信看下面:
这样子搜索入度为 0 的点实在太憨了,浪费时间,借助队列可以节省时间。就是先把度为 0 的节点入队,之后队列出队,对出队的节点的后继节点的入度减 1,注意减到 0 就要入队,如此循环。最后队列空了,即没有入度为 0 的点了,循环结束,如果结果中的点没有包含全部节点,说明有环。去掉搜索 0 入度的成员函数,修改拓扑排序的函数如下。
vector<string> Graph::topsort() {
vector<string> topsort_result;
queue<int> Q;//辅助队列
for (int i = 0; i < node_num; ++i) {
if (indegree[i] == 0) Q.push(i);
}
while (!Q.empty()) {
int cur = Q.front();
Q.pop();
topsort_result.push_back(index2node[cur]);
for (auto index : adjacencylist[cur]) {
if (--indegree[index] == 0)
Q.push(index);
}
}
if (topsort_result.size() != node_num) {
cout << "有环" << endl;
}
return topsort_result;
}
最短路径算法
无人驾驶中有什么路径规划,什么全局路径规划,局部路径规划,计算机网络讲网络层的时候有什么路由选择算法,其实归根结底都可以理解为求最短路径的问题(反正我是这么理解的),当然要达到这个最短可以考虑时间、空间,无人驾驶上还考虑路径的拥堵程度,还有其他各种东西,这里就把这一切东西都算到所谓的“权”上(无权图怎么办?无权图虽然边上没有权,路径长短只和边的数量有关,那就不妨想象全体边的权为1呗)。
Dijkstra算法(还没写全)
这个算法我每次看懂后过会就忘,没办法,算法这种东西还是得多用才能记住。这里就再总结一下理论,写点代码吧。
来看一个有向带权图及对应的邻接表:
若要知晓某个点(源节点,这里不妨设为 A),到其他各个节点的最短路径,可以用Dijkstra算法实现。设节点数量为 N,该算法通过 N 次迭代即可得到源节点到全体节点的最短路径及对应的长度(权值和)。在迭代的过程中不断更新源节点到其他每个节点的路径长度d,路径是否已知K,以及每个目标节点的前驱节点p。首先初始化这些信息,这里大家一开始都没有前驱,出来源节点本身外,其他节点对应的路径长度都是无穷大。之后每次迭代都找出路径最短的点,更新其相邻节点的信息。从这里可以用一个表来容纳这些信息,算法的执行过程如下。
这里最后一次迭代完成后,源节点到其他每个节点的最短路径长度就都知道了,并且由于每个节点的前驱节点也都记录了,因此源节点到其他每个节点的最短路径也能方便得得到。
写点C++代码。由于每次迭代都要找路径最短的节点,所以不妨构建一个小顶堆,这样子找最小值方便,不过更新值就麻烦了。对于程序输入,不仅要和之前一样输入边,还要输入权值。另外现在还带有权值了,之前写的沙雕代码又要改改了,哭了。
/*
此处正在维护,请稍等。。。。。
*/
(未完待续哈哈哈!)