这是我发现的难得的比较好文章,介绍Dijkstra 算法的c++程序部署。

Dijkstra's algorithm is a graph algorithm that simultaneously finds the shortest path from a single vertex in a weighted graph to all other vertices in the graph, called the single-source shortest path problem. It works for directed and undirected graphs, but unlike the Bellman-Ford algorithm, requires nonnegative edge weights.

Contents

[hide]

  • 1 Simple graph representation
  • 2 Main algorithm
  • 3 Sample code
  • 4 Alternatives
[edit] Simple graph representation

The data structures supplied by C++'s Standard Template Library facilitate a straightforward implementation of Dijkstra's algorithm. For simplicity, we start with a simple graph representation where the vertices are numbered by sequential integers (0, 1, 2, ...) and the edge weights are doubles. We define a struct representing an edge that stores its weight and target vertex (the vertex it points to):

<<simple graph types>>=
typedef int vertex_t;
typedef double weight_t;
 
struct edge {
    const vertex_t target;
    const weight_t weight;
    edge(vertex_t arg_target, weight_t arg_weight)
        : target(arg_target), weight(arg_weight) { }
};

Because we'll need to iterate over the successors of each vertex, we will find an adjacency list representation most convenient. We will represent this as an adjacency map, a mapping from each vertex to the list of edges exiting that vertex, as defined by the following typedef:

<<simple graph types>>=
typedef std::map<vertex_t, std::list<edge> > adjacency_map_t;
 
<<definition headers>>=
#include <map>
#include <list>
[edit] Main algorithm

We're now prepared to define our method. We separate the computation into two stages:

  1. Compute the minimum distance from the source to each vertex in the graph. Simultaneously, keep track of the previous map, which for each vertex v gives the previous vertex on the shortest path from the source vertex to v. This is the expensive step.
  2. Later, any time we want to find a particular shortest path between the source vertex and a given vertex, we use the previous array to quickly construct it.

For the first part, we write DijkstraComputePaths, which takes two input parameters and two output parameters:

  • source (in): The source vertex from which all shortest paths are found
  • adjacency_map (in): The adjacency map specifying the connection between vertices and edge weights.
  • min_distance (out): Will receive the shortest distance from source to each vertex in the graph.
  • previous (out): Receives a map that can be passed back into DijkstraGetShortestPathTo() later on to quickly get a shortest path from the source vertex to any desired vertex.
<<simple compute paths function>>=
void DijkstraComputePaths(vertex_t source,
                          const adjacency_map_t &adjacency_map,
                          std::map<vertex_t, weight_t> &min_distance,
                          std::map<vertex_t, vertex_t> &previous)
{
    initialize output parameters
min_distance[source] = 0;
visit each vertex u, always visiting vertex with smallest min_distance first
// Visit each edge exiting u
const std::list<edge> &edges = adjacency_map.find(u)->second;
for (std::list<edge>::const_iterator edge_iter = edges.begin();
edge_iter != edges.end();
edge_iter++)
{
vertex_t v = edge_iter->target;
weight_t weight = edge_iter->weight;
relax the edge (u,v)
}
}
}

The outline of how the function works is shown above: we visit each vertex, looping over its out-edges and adjusting min_distance as necessary. The critical operation is relaxing the edges, which is based on the following formula:

if (u, v) is an edge and u is on the shortest path to v, d(u) + w(u,v) = d(v).

In other words, we can reach v by going from the source to u, then following the edge (u,v). Eventually, we will visit every predecessor of v reachable from the source. The shortest path goes through one of these. We keep track of the shortest distance seen so far by setting min_distance and the vertex it went through by setting previous:

<<relax the edge (u,v)>>=
weight_t distance_through_u = min_distance[u] + weight;
if (distance_through_u < min_distance[v]) {
    remove v from queue
min_distance[v] = distance_through_u;
previous[v] = u;
re-add v to queue
}

Note that the original description of Dijkstra's algorithm involves remembering that when a vertex is "done" (i.e. we have gone through an iteration of the loop with u = that vertex), we put it in a set of "visited" vertices, so that if we reach it again we can skip it. However, it is not really necessary to do this specially, as it is guaranteed that if we re-reach a "done" vertex, the new distance to it will never be lessthan its original min distance, so the relaxation condition will always fail in this case which will also cause it to be skipped.

We also need to initialize the output parameters so that at first all min distances are positive infinity (as large as possible). We can visit all vertices by iterating over the keys of adjacency_map, and then iterating over the elements in the values of adjacency_map. This is needed since this is a directed graph, and so there may be vertices without any outgoing edges (i.e. sinks) whose min distance still must be set to positive infinity.

<<initialize output parameters>>=
for (adjacency_map_t::const_iterator vertex_iter = adjacency_map.begin();
     vertex_iter != adjacency_map.end();
     vertex_iter++)
{
    vertex_t v = vertex_iter->first;
    min_distance[v] = std::numeric_limits< double >::infinity();
    for (std::list<edge>::const_iterator edge_iter = vertex_iter->second.begin();
         edge_iter != vertex_iter->second.end();
         edge_iter++)
    {
        vertex_t v2 = edge_iter->target;
        min_distance[v2] = std::numeric_limits< double >::infinity();
    }
}
<<definition headers>>=
#include <limits> // for numeric_limits

Finally, we need a way to visit the vertices in order of their minimum distance. We could do this using a heap data structure, but later on we will also need to decrease the distance of particular vertices, which will involve re-ordering that vertex in the heap. However, finding a particular element in a heap is a linear-time operation. To avoid this, we instead use a self-balancing binary search tree, which will also allow us to find the vertex of minimum distance, as well as find any particular vertex, quickly. We will use a std::set of pairs, where each pair contains a vertex v along with its minimum distancemin_distance[v]. Fortunately, C++'s std::pair class already implements an ordering, which orders lexicographically (by first element, then by second element), which will work fine for us, as long as we use the distance as the first element of the pair.

We don't need to put all the vertices into the priority queue to start with. We only add them as they are reached.

<<visit each vertex u, always visiting vertex with smallest min_distance first>>=
std::set< std::pair<weight_t, vertex_t> > vertex_queue;
vertex_queue.insert(std::make_pair(min_distance[source], source));
 
while (!vertex_queue.empty()) 
{
    vertex_t u = vertex_queue.begin()->second;
    vertex_queue.erase(vertex_queue.begin());
 
 
<<definition headers>>=
#include <set>
#include <utility> // for pair

We access and remove the smallest element using begin(), which works because the set is ordered by minimum distance. If we change a vertex's minimum distance, we must update its key in the map as well (if we are reaching the vertex for the first time, it won't be in the queue, and erase() will do nothing):

<<remove v from queue>>=
vertex_queue.erase(std::make_pair(min_distance[v], v));
 
<<re-add v to queue>>=
vertex_queue.insert(std::make_pair(min_distance[v], v));

This completes DijkstraComputePaths(). DijkstraGetShortestPathTo() is much simpler, just following the linked list in the previous map from the target back to the source:

<<get shortest path function>>=
std::list<vertex_t> DijkstraGetShortestPathTo(
    vertex_t target, const std::map<vertex_t, vertex_t> &previous)
{
    std::list<vertex_t> path;
    std::map<vertex_t, vertex_t>::const_iterator prev;
    vertex_t vertex = target;
    path.push_front(vertex);
    while((prev = previous.find(vertex)) != previous.end())
    {
        vertex = prev->second;
        path.push_front(vertex);
    }
    return path;
}
[edit] Sample code

Here's some code demonstrating how we use the above functions:

<<dijkstra_example.cpp>>=
#include <iostream>
#include <vector>
#include <string>
definition headers
simple graph types
simple compute paths function
get shortest path function
int main()
{
adjacency_map_t adjacency_map;
std::vector<std::string> vertex_names;
initialize adjacency map
std::map<vertex_t, weight_t> min_distance;
std::map<vertex_t, vertex_t> previous;
DijkstraComputePaths(0, adjacency_map, min_distance, previous);
print out shortest paths and distances
return 0;
}

Printing out shortest paths is just a matter of iterating over the vertices and callingDijkstraGetShortestPathTo() on each:

<<print out shortest paths and distances>>=
for (adjacency_map_t::const_iterator vertex_iter = adjacency_map.begin();
     vertex_iter != adjacency_map.end();
     vertex_iter++)
{
    vertex_t v = vertex_iter->first;
    std::cout << "Distance to " << vertex_names[v] << ": " << min_distance[v] << std::endl;
    std::list<vertex_t> path =
        DijkstraGetShortestPathTo(v, previous);
    std::list<vertex_t>::const_iterator path_iter = path.begin();
    std::cout << "Path: ";
    for( ; path_iter != path.end(); path_iter++)
    {
        std::cout << vertex_names[*path_iter] << " ";
    }
    std::cout << std::endl;
}

For this example, we choose vertices corresponding to some East Coast U.S. cities. We add edges corresponding to interstate highways, with the edge weight set to the driving distance between the cities in miles as determined by Mapquest (note that edges are directed, so if we want an "undirected" graph, we would need to add edges going both ways):

<<initialize adjacency map>>=
vertex_names.push_back("Harrisburg");   // 0
vertex_names.push_back("Baltimore");    // 1
vertex_names.push_back("Washington");   // 2
vertex_names.push_back("Philadelphia"); // 3
vertex_names.push_back("Binghamton");   // 4
vertex_names.push_back("Allentown");    // 5
vertex_names.push_back("New York");     // 6
adjacency_map[0].push_back(edge(1,  79.83));
adjacency_map[0].push_back(edge(5,  81.15));
adjacency_map[1].push_back(edge(0,  79.75));
adjacency_map[1].push_back(edge(2,  39.42));
adjacency_map[1].push_back(edge(3, 103.00));
adjacency_map[2].push_back(edge(1,  38.65));
adjacency_map[3].push_back(edge(1, 102.53));
adjacency_map[3].push_back(edge(5,  61.44));
adjacency_map[3].push_back(edge(6,  96.79));
adjacency_map[4].push_back(edge(5, 133.04));
adjacency_map[5].push_back(edge(0,  81.77));
adjacency_map[5].push_back(edge(3,  62.05));
adjacency_map[5].push_back(edge(4, 134.47));
adjacency_map[5].push_back(edge(6,  91.63));
adjacency_map[6].push_back(edge(3,  97.24));
adjacency_map[6].push_back(edge(5,  87.94));
[edit] Alternatives

In an application that does a lot of graph manipulation, a good option is the Boost graph library, which includes support for Dijkstra's algorithm.

 

我修改的代码:

   1: #include <iostream>
   2: #include <vector>
   3: #include <iterator>
   4: #include <fstream>
   5: #include <sstream>
   6: #include <string>
   7: //#include <memory>
   8: #include <map>
   9: #include <list>
  10: #include <set>
  11: #include <algorithm>
  12: //#include <queue>          // priority_queue
  13: //#include <functional>     // greater
  14:  
  15: using namespace std;
  16: typedef int vertex_t;
  17: typedef int weight_t;
  18:  
  19: struct edge
  20: {
  21:     const vertex_t target;
  22:     const weight_t weight;
  23:     edge (vertex_t arg_target, weight_t arg_weight)
  24:         : target(arg_target), weight( arg_weight) { } 
  25: };
  26:  
  27: typedef map<vertex_t, list<edge>> adjacency_map_t;
  28:  
  29:  
  30: void DijkstraComputePaths(vertex_t source,
  31:                           const adjacency_map_t &adjacency_map,
  32:                           map<vertex_t, weight_t> &min_distance,
  33:                           map<vertex_t, vertex_t> &previous);
  34: istream& readData(istream &in, adjacency_map_t &adjacency_map);
  35: void showResult(const vector<int> &vec,  map<vertex_t, weight_t> &min_distance);
  36:  
  37: int main(){
  38:     //fstream fin ("data2.txt");
  39:     fstream fin("dijkstraData.txt");
  40:     adjacency_map_t adjacency_map;
  41:     readData(fin, adjacency_map);
  42:  
  43:     map<vertex_t, weight_t> min_distance;
  44:     map<vertex_t, vertex_t> previous;
  45:  
  46:     DijkstraComputePaths(1, adjacency_map, min_distance, previous);
  47:     
  48:     int tmp[] = {7, 37, 59, 82, 99, 115, 133, 165, 188, 197};
  49:     vector<int> vec_res(tmp, tmp + sizeof(tmp) / sizeof(int)  );
  50:     showResult(vec_res, min_distance);
  51:     return 0;
  52: }
  53:  
  54:  
  55:  
  56:  
  57: void showResult(const vector<int> &vec,  map<vertex_t, weight_t> &min_distance){
  58:     for (auto iter = vec.cbegin(); iter != vec.cend(); iter++){
  59:         cout << min_distance[*iter] << ",";
  60:     }
  61: }
  62:  
  63:  
  64: istream& readData(istream &in, adjacency_map_t &adjacency_map){
  65:     string line;
  66:     vertex_t vertex1, vertex2;
  67:     weight_t weight;    
  68:     //istringstream ss;
  69:     
  70:     while ( getline(in, line)){
  71:         istringstream ss(line);
  72:         ss >> vertex1;
  73:         while ( ss >> vertex2 >> weight){
  74:             adjacency_map[vertex1].push_back( edge(vertex2, weight));
  75:             adjacency_map[vertex2].push_back( edge(vertex1, weight));
  76:         }            
  77:     }
  78:     cout << "Reading data completes" << endl;
  79:     return in; 
  80: }
  81:  
  82:  
  83: void DijkstraComputePaths(vertex_t source,
  84:                           const adjacency_map_t &adjacency_map,
  85:                           map<vertex_t, weight_t> &min_distance,
  86:                           map<vertex_t, vertex_t> &previous)
  87: {
  88:     //map<vertex_t, bool> explorerd;
  89:     for (auto vertex_iter = adjacency_map.begin(); vertex_iter != adjacency_map.end(); vertex_iter++)
  90:     {
  91:         vertex_t v = vertex_iter->first;
  92:         min_distance[v] = 1000000;
  93:         //explorerd[v] = false;
  94:         // for (auto edge_iter = vertex_iter->second.begin();
  95:         //      edge_iter != vertex_iter->second.end();
  96:         //      edge_iter++)
  97:         // {
  98:         //     vertex_t v2 = edge_iter->target;
  99:         //     min_distance[v2] = 1000000;
 100:         // }
 101:     }
 102:     min_distance[source] = 0;
 103:     set< pair<weight_t, vertex_t> > vertex_queue;
 104:  
 105:     vertex_queue.insert(make_pair(min_distance[source], source));
 106:     while (!vertex_queue.empty()) 
 107:     {
 108:         vertex_t u = vertex_queue.begin()->second;
 109:         vertex_queue.erase(vertex_queue.begin());
 110:  
 111:  
 112:         // Visit each edge exiting u
 113:         const list<edge> &edges = adjacency_map.find(u)->second;
 114:         for (auto edge_iter = edges.begin();
 115:              edge_iter != edges.end();
 116:              edge_iter++)
 117:         {
 118:             vertex_t v = edge_iter->target;
 119:             //if (explorerd[v]) continue;
 120:             //explorerd[v] = true;
 121:             weight_t weight = edge_iter->weight;
 122:             weight_t distance_through_u = min_distance[u] + weight;
 123:             if (distance_through_u < min_distance[v]) {
 124:                 vertex_queue.erase(make_pair(min_distance[v], v));
 125:                 min_distance[v] = distance_through_u;
 126:                 previous[v] = u;
 127:                 vertex_queue.insert(make_pair(min_distance[v], v));
 128:             }
 129:         }
 130:     }
 131: }

 

测试数据

//data1.txt

1 2,3 3,3

2 3,1 4,2

3 4,50

4

//result

1 0 []

2 3 [2]

3 3 [3]

4 5 [2, 4]

转载于:https://www.cnblogs.com/lab009/p/3253329.html

Dijkstra's algorithm (C++)相关推荐

  1. 广度优先搜索(BreadthFirstSearch) 迪克斯特拉算法 (Dijkstra's algorithm)

    BFS可回答两类问题: 1.从节点A出发,有前往节点B的路径吗? 2.从节点A出发,前往节点B的哪条路径经过的节点最少? BFS中会用到"队列"的概念.队列是一种先进先出(FIFO ...

  2. 迪克斯特拉算法--Dijkstra's Algorithm

    在图形应用中,常常需要求从图中某个结点至其余各结点的最短路径,如对于一个物流配送系统计算从配送中心到各订货点的最短路径. Dijkstra's Algorithm 基本思想: 若给定带权有向图G=(V ...

  3. 迪克斯特拉算法-- Dijkstra's Algorithm

    在图形应用中,常常需要求从图中某个结点至其余各结点的最短路径,如对于一个物流配送系统计算从配送中心到各订货点的最短路径. Dijkstra's Algorithm 基本思想: 若给定带权有向图G=(V ...

  4. 迪杰斯特拉算法(Dijkstra‘s algorithm)以及示例

    迪杰斯特拉算法(Dijkstra's algorithm)是一种非常重要且有价值的算法.它被广泛应用于计算图中单源最短路径问题,在交通路线规划.网络路由.作业调度等领域有着广泛的应用. 迪杰斯特拉算法 ...

  5. python求多条最短路径_Python实现最短路径算法(Dijkstra's algorithm)

    给出的图G赋权连接矩阵为: adjacency_matrix = [ [0, 1, 10, -1, -1, 2], [10, 0, 1, -1, -1, -1], [1, 10, 0, -1, -1, ...

  6. JAVA[Algorithm]--戴克斯特拉

    2019独角兽企业重金招聘Python工程师标准>>> 一.算法介绍: 戴克斯特拉算法(Dijkstra's algorithm)是由荷兰计算机科学家艾兹赫尔·戴克斯特拉提出.迪科斯 ...

  7. 最短路径算法——Dijkstra and Floyd算法

    一.     前言:     这个古老的算法应该耳熟能详了吧,但是我自从从学校出来到现在,最短路径算法都没有实际运用过,最近在一个GIS项目中总算用到了,于是乎把教材重温了下,同时查阅了网上很多的资料 ...

  8. Dijkstra 算法

    dijkstra算法,最简单的实现需要$O(|V|^2)$.用binary heap实现,可以优化到O((|V|+|E|)lg|V|),如果用fibonacci heap的话,可以优化到O(|E|+| ...

  9. Dijkstra算法的C语言程序

    Dijkstra算法用来寻找图的结点间最短路径,通常是指定一个起始结点后,寻找从该结点出发,到达各个结点的最短路径.该算法是有关最短路径问题的一个算法.由Dijkstra于1959年提出. 百度百科: ...

最新文章

  1. 红帽linux登陆错误,xmanager连接RedHat出错:/usr/X11R6/bin/xterm: No such file or directory
  2. 第十次作业是同一个人
  3. 全卷积神经网路【U-net项目实战】U-Net网络练习题: Kaggle - 2018 Data Science Bowl
  4. 天刀各大区服务器位置,数据帝玩家统计 天刀全区活跃地图展示
  5. what you should do when you are alone
  6. 智能安防新时代:用户、SI、设备制造商要做什么?
  7. python发红包问题_一个关于红包的问题引发的python算法初体验
  8. android 模块自动加载,Android 之ko模块的自动加载
  9. Java面向对象的三大特征(封装,继承,多态)
  10. STF简单修改实现安卓多机同屏控制
  11. 【白皮书分享】2020年度薪酬白皮书.pdf(附下载链接)
  12. android:Read-only file system解决
  13. SVN版本控制介绍与使用(超详细版)
  14. 电商数据分析与数据化运营
  15. python request下载文件时、显示进度以及网速_实时网速显示_实例_python
  16. Flutter强制某个页面横屏
  17. 计算机图形学 opengl版本 第三版------胡事民 第三章更多的绘图工具
  18. ISDP_ch10ch11问题汇总
  19. 2013年全球最值得模仿的10个社交类网站应用
  20. 第九回(二):任侠厨子轻解厄围 夜半蹴鞠为泄忿闷[林大帅作品集]

热门文章

  1. 简易HTTP协议解析
  2. java加密字符串,可解密
  3. SpringBoot项目redis的消息队列
  4. endnote初始化数据库支持_5 个免费的在线 SQL 数据库环境,比Navicat 香
  5. ds18b20温度转换指令_【Proteus】DS18B20简易温控器
  6. java注册头像_注册页面头像上传的实现(javaweb相关)
  7. hue 添加jar_在hue下配置jdbc驱动
  8. 北邮计算机学不学单片机,北邮小学期PC单片机.docx
  9. 提交不了_从来没有借过钱!征信空白,为什么办不了信用卡?
  10. java 传递函数_java传递函数参数(值传递)