Dijkstra存储多条相同最短路径
使用list结构存储多个顶点(预备)
完整源码
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> larray[100];
larray[0].push_back(0);
larray[0].push_back(1);
larray[1].push_back(10);
larray[1].push_back(20);
larray[1].push_back(30);
for(int i = 0 ; i <= 1; i++)
{
list<int> mylist = larray[i];
for(list<int>::iterator ii = mylist.begin(); ii != mylist.end(); ii++)
cout << *ii << " ";
cout << endl;
}
}
测试运行
0 1
10 20 30
代码说明
- 这里把list数组的创建、添加、访问数据的方法先特别摘出来,是为了等下拼到原始的
Dijkstra算法
代码中保持头脑清晰; - 原始的
Dijstra算法
计算最短路径问题,只存储一条路径的实现(点我),也可见引用资料[0];
Dijkstra算法计算、存储多条最短路径(正题)
完整源码
#include <iostream>
#include <vector>
#include <tuple>
#include <queue>
#include <stack>
#include <map>
#include <list>
using namespace std;
double distTo[100];
//int edgeTo[100];
list<int> edgeTo[100];
int V, E;
const double INF_MAX = 9999999.9;
map<int , vector<tuple<int, int, double>>> EWD;
stack<int> path;
int coutSP = -1;
struct GreaterThanByDist
{
bool operator()(const int i, const int j) const
{
return distTo[i] > distTo[j];
}
};
priority_queue<int, vector<int>, GreaterThanByDist> Minpq;
void relax(tuple<int, int, double> edge)
{
int v = get<0>(edge);
int w = get<1>(edge);
double weight = get<2>(edge);
if (distTo[w] > distTo[v] + weight) {
distTo[w] = distTo[v] + weight;
//edgeTo[w] = v;
edgeTo[w].clear();
edgeTo[w].push_back(v);
Minpq.push(w);
}
else if(distTo[w] == distTo[v] + weight)
{
edgeTo[w].push_back(v);
}
}
void DijkstraSP(int s,int V)
{
for(int v = 0 ; v < V; v++)
distTo[v] = INF_MAX;
distTo[s] = 0.0;
Minpq.push(s);
while(!Minpq.empty())
{
int v = Minpq.top();
Minpq.pop();
for(vector<tuple<int, int, double>>::iterator ii = EWD[v].begin();
ii != EWD[v].end();
ii++)
{ relax(*ii); }
}
}
void dfs(int source, int vertex)
{
coutSP++;
if(vertex == source) coutSP = 0;
for(list<int>::iterator ii = edgeTo[vertex].begin(); ii != edgeTo[vertex].end(); ii++)
{
if(coutSP == 0) path.push(vertex);
path.push(*ii);
dfs(source, *ii);
}
}
void computeSP(int source, int vertex)
{
cout << "shortest path : ";
DijkstraSP(source, V);
cout << source << " to " << vertex << " ( " << distTo[vertex] << " ) " << " : " ;
dfs(source , vertex);
while(!path.empty())
{
cout << path.top() << " ";
path.pop();
}
cout << endl;
}
void showEWD()
{
cout << "EdgeWeightedDigraph : " << endl;
for(int v = 0; v < V; v++)
{
cout << v << " : ";
for(vector<tuple<int, int, double>>::iterator ii = EWD[v].begin();
ii != EWD[v].end();
ii++)
{ cout << get<0>(*ii) << "->" << get<1>(*ii) << " " << get<2>(*ii) << " "; }
cout << endl;
}
}
int main()
{
cin >> V >> E;
for(int i = 0 ; i < E ;i++)
{
int v, w;
double weight;
cin >> v >> w >> weight;
EWD[v].push_back(make_tuple(v, w, weight));
}
//showEWD();
int source, vertex;
cout << "source : ";
cin >> source;
cout << "vertex : ";
cin >> vertex;
computeSP(source, vertex);
system("pause");
}
模拟数据
测试运行
9
11
0 1 0.1
0 2 0.1
0 3 0.1
1 4 0.1
1 5 0.1
2 6 0.1
3 7 0.1
4 8 0.1
5 8 0.1
6 8 0.1
7 8 0.1
source : 0
vertex : 8
shortest path : 0 to 8 ( 0.3 ) :
0 1 5 8
0 3 7 8
0 2 6 8
0 1 4 8
详细说明
引入的标准
#include <list>
存储多个顶点
void relax(tuple<int, int, double> edge)
{
int v = get<0>(edge);
int w = get<1>(edge);
double weight = get<2>(edge);
if (distTo[w] > distTo[v] + weight) {
distTo[w] = distTo[v] + weight;
//edgeTo[w] = v;
edgeTo[w].clear();
edgeTo[w].push_back(v);
Minpq.push(w);
}
else if(distTo[w] == distTo[v] + weight)
{
edgeTo[w].push_back(v);
}
}
- 每次遇到 更短 的最短路径,要清空
list
,使用这条语句edgeTo[w].clear();
; - 如果遇到 相同 的最短路径,只需要把顶点加入到
list
即可,else if {}
是新增的;
DFS遍历多条路径
int coutSP = -1;
void dfs(int source, int vertex)
{
coutSP++;
if(vertex == source) coutSP = 0;
for(list<int>::iterator ii = edgeTo[vertex].begin(); ii != edgeTo[vertex].end(); ii++)
{
if(coutSP == 0) path.push(vertex);
path.push(*ii);
dfs(source, *ii);
}
}
- 变量
coutSP
的作用是一个flag
,告诉自己一条最短路径已经遍历完毕(回到了source
); - 预期输出的是一条完整的最短路径,比如从起点0一直-> -> ->到终点8,所以本质上终点8有必要在每次进入到新的一条最短路径的遍历时都再被插入(到path)一次,结合变量
coutSP
就可以做到了;
关于选择list的原因
list
起到的就是一个存储多个顶点的作用,C++ STL
的模板如此丰富,用stack
啊,queue
啊,甚至vector
还有map
都可以实现这个,选list
,只是为了和代码中已经用到的vector
以及map
做个区分,以为我用map vector
以及tuple
做了邻接表表示法的带权有向图。如果不需要保存多条最短路径,删代码的时候就很好删除了。
关于数据的输出效果
实际上,代码运行输出的结果会是一行的0 1 5 8 0 3 7 8 0 2 6 8 0 1 4 8
,上面为了看上去效果好一点,自己做了换行,实际上我把全部的路径都压到一个stack
里面,如果需要代码实际输出效果更好看,根据输出的内容来改就好,比如遇到起点source
的时候加个flag(打开)
,遇到终点8
让flag(关闭)
,这时候在输出一个换行什么的,因为和本文的主要内容不太相关,这里就略过。
引用资料
[0] [C++]C++ STL Dijkstra算法 带权有向图(邻接表)单源最短路径求解
http://blog.csdn.net/cook2eat/article/details/53912885[1]C++ STL
list begin()
http://www.cplusplus.com/reference/list/list/begin/list push_back()
http://www.cplusplus.com/reference/list/list/push_back/list clear()
http://www.cplusplus.com/reference/list/list/clear/