[C++]C++ STL Dijkstra算法 带权有向图(邻接表)单源最短路径求解
2016-12-28 22:17
721 查看
单源最短路径问题求解
带权有向图(邻接表表示法)
完整源码
#include <iostream> #include <vector> #include <tuple> #include <map> using namespace std; map<int , vector<tuple<int, int, double>>> EWD; int main() { int V, E; 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)); } 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; } system("pause"); }
测试用例
测试数据来源见引用[1] 文件结构: V //顶点数 E // 边数 v w weight // 顶点 顶点 权重 数据内容: 8 15 4 5 0.35 5 4 0.35 4 7 0.37 5 7 0.28 7 5 0.28 5 1 0.32 0 4 0.38 0 2 0.26 7 3 0.39 1 3 0.29 2 7 0.34 6 2 0.40 3 6 0.52 6 0 0.58 6 4 0.93
运行结果
EdgeWeightedDigraph : 0 : 0->4 0.38 0->2 0.26 1 : 1->3 0.29 2 : 2->7 0.34 3 : 3->6 0.52 4 : 4->5 0.35 4->7 0.37 5 : 5->4 0.35 5->7 0.28 5->1 0.32 6 : 6->2 0.4 6->0 0.58 6->4 0.93 7 : 7->5 0.28 7->3 0.39
代码说明
带权重边的表示//引入 C++ STL tuple 类型 #include <tuple> //顶点v -> 顶点w 权重weight tuple<int, int, double> //创建tuple对象 make_tuple(v, w, weight) //获取tuple类型中的数据 get<0>(a_tuple_object) // v get<1>(a_tuple_object) // w get<2>(a_tuple_object) // weight
有向图的表示
// 引入 vector 类型 和 map #include <vector> #include <map> // 邻接表表示法 表示 有向图 // int : 顶点编号 // vector : 一个带权有向边的集合 map<int , vector<tuple<int, int, double>>> EWD; // 向图中添加边 // 邻接表表示法(直观点,看上方运行结果) EWD[v].push_back(make_tuple(v, w, weight));
关于抽象的理解
用邻接表表示图,代码实现是一个
map(哈希表);
用每个顶点编号哈希索引出来的都是一个
vector列表,列表本质是一些带权重的有向边;
带权重的有向边包括:
v顶点(
from),
w顶点(
to)以及权重
weight,代码实现是一个
tuple,就是把三个不同类型的数据“绑”在一起;
Dijkstra 算法求解单源最短路径问题
使用条件
Dijstra算法求解单源最短路径问题,要求边权重非负。算法思想
Dijkstra's algorithm initializing dist[s] to 0 and all other distTo[] entries to positive infinity. Then, it repeatedly relaxes and adds to the tree a non-tree vertex with the lowest distTo[] value, continuing until all vertices are on the tree or no non-tree vertex has a finite distTo[] value.(参考[2])
简单翻译
生成一个最短路径树:1. 首先指定一个 起点 ,将 起点 加入最短路径树,放松 起点 指出的所有边;
2. 选取 最短路径树顶点集合 指出的所有边 中离起点最近的那条,将 那条边 指向的 非树顶点 加入最短路径树,放松 其 指出 所有的边;
3. 重复步骤2;
4. 直到 所有顶点 加入最短路径树,算法终止。
//初始条件 dist[source] = 0; // 起点的距离置为 0 dist[othetrs] = +∞; // 说明 distTo[w] // from s to w , sum of weight edgeTo[w] // last edge to w Minpq 见[3] // relax 放松 假设起点 s 到达某个顶点 w 的distTo[w]已知了, 不妨看做是s->w ; 这时候发现如果 s 绕路到 v 再去 w 会更“近”,那么就走s->v->w,更新distTo[w],edgeTo[w] = v ; 直到从s出发可达的全部顶点的最短路径计算完毕,算法终止.
再次强调
distTo[]记录的是,到达w为止的总长度;
edgeTo[]记录的是,路径上的最后一个顶点(在algs4的实现代码中,记录的是最后一条边edge);
完整源码
参考algs4 DijkstraSP.java实现 具体见参考[2]#include <iostream> #include <vector> #include <tuple> #include <queue> #include <stack> #include <map> using namespace std; double distTo[100]; int edgeTo[100]; int V, E; const double INF_MAX = 9999999.9; map<int , vector<tuple<int, int, double>>> EWD; 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; Minpq.push(w); } } 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 computeSP(int source, int vertex) { cout << "shortest path : "; DijkstraSP(source, V); cout << source << " to " << vertex << " ( " << distTo[vertex] << " ) " << " : " ; stack<int> path; for(int i = vertex; i != source; i = edgeTo[i]) path.push(i); path.push(source); 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; for(int i = 0; i < V; i++) computeSP(source, i); system("pause"); }
运行结果
source : 0 vertex : 7 shortest path : 0 to 0 ( 0 ) : 0 shortest path : 0 to 1 ( 1.05 ) : 0 4 5 1 shortest path : 0 to 2 ( 0.26 ) : 0 2 shortest path : 0 to 3 ( 0.99 ) : 0 2 7 3 shortest path : 0 to 4 ( 0.38 ) : 0 4 shortest path : 0 to 5 ( 0.73 ) : 0 4 5 shortest path : 0 to 6 ( 1.51 ) : 0 2 7 3 6 shortest path : 0 to 7 ( 0.6 ) : 0 2 7
代码心得
求解单源最短路径问题就是生成一颗单源最短路径树,选定起点,从集合的角度看就可以分成树-顶点(起点为首), 非树-顶点两个集合;着眼于顶点,下一个要从非树-顶点集合中被挑选加入树-顶点集合的顶点,是距离起点最近的那个点;
一个点被选中后 (体现在代码是从优先队列被删除 ),马上接着这个点指出的所有边需要被放松 ;
正因为有上面的放松动作,才为下一个待选顶点创造了比较的依据。
MST 最小生成树问题与SP最短路径简单比较
图例
对比Prim算法求解MST 和Dijkstra算法求解SP
Prime算法,可以这样理解,首先任意选定顶点
{0},距离这个集合最近的边比如
0-1边被选入,集合变成
{0,1},接着距离这个集合最近的边比如
0-3边被选入,集合变成
{0,1,3},最后距离这个集合最近的边
2-3边被选择,集合成为
{0,1,2,3},算法终止。
Dijkstra算法,可以这样理解,首先必须指定起点
{0},距离起点最近的顶点1被选入,集合变成
{0,1},然后距离起点最近的是顶点3,,集合变成
{0,1,3},最后距离起点最近的是顶点2,集合变成
{0,1,2,3};
虽然从集合的角度,加入的顶点顺序都是一样的,但是最后选定的边和选取顶点的规则不同 :
MST最小生成树看的是新加入的顶点到集合这个整体的距离;
SP最短路径看的是顶点到起点的距离;
0直接到2的距离1.5明显比经由0-3-2的走法要距离起点0更近,在本例中就是不会被放松的情况;
如果从3到2的距离是0.1,那么很明显本次的MST和SP最终的结果选定的边就会相同;
实际上,
Dijkstra只要舍去放松操作,就可以看做是
Prim算法,能够拿来求解MST
总结:
MST最小生成树 并不能保证任意两点之间距离最短 ,MST追求永远的总的权重最小,也就是走遍全部顶点的路径长度最短;
SP最短路径或者更准确的说法是 某个指定的起点到达其他点的路径最短 ,追求的是从某个具体点出发, 这个具体点到达任意点的路径都是最短的;
MST和
SP最终的边的集合是有可能相同的,但是这是一种巧合。
资料引用
[1] http://algs4.cs.princeton.edu/44sp/tinyEWD.txt[2] DijkstraSP.java
http://algs4.cs.princeton.edu/44sp/
http://algs4.cs.princeton.edu/44sp/DijkstraSP.java
[3] [C++]C++ STL priority_queue IndexPriorityQueue 索引优先队列 比较器
http://blog.csdn.net/cook2eat/article/details/52455647
相关文章推荐
- 单源最短路径 C++实现 调试通过
- 图(有向图)的邻接表表示 C++实现(遍历,拓扑排序,最短路径,最小生成树) Implement of digraph and undigraph using adjacency list
- 单源最短路径—Dijkstra算法(C++)
- dijkstra算法求解单源点最短路径
- 邻接表实现 单源最短路径SPFA算法
- Dijkstra求解单源点最短路径
- [置顶] 求解单源最短路径:Dijkstara算法典型示例
- Dijkstra算法讲解(单源最短路径问题求解)
- 运用Dijkstra算法求得单源最短路径C/C++
- 贪心法求解单源最短路径问题
- 邻接表实现 单源最短路径SPFA算法 poj1511
- Dijkstra单源最短路径算法; 优先队列+静态数组邻接表; STL优先队列还是没想明白排序原则;
- 邻接表实现,单源最短路径
- 图 单源最短路径求解 Dijkstra算法 C++ 实现
- Dijkstra算法(单源最短路径) C++
- 利用邻接表求解所有节点的最短路径 java实现 可运行
- 图Floyd 多源最短路径求解 C++实现
- 用c++代码实现贪心算法求解最短路径问题
- Dijkstra算法 求单源含权最短路径(邻接表有向图)C实现
- 【算法】图的应用之Dijkstra算法--单源最短路径的求解