Dijkstra算法的C++实现

之前的讨论了无权图的最短路径算法。数据结构与算法——无权最短路径算法的C++实现

如果是加权图,那么问题就变得困难了,不过仍然可以采用无权情况的想法。

我们仍然保留之前的信息。因此,每个顶点会被标记为known或unknown,每个顶点保留一个尝试性的距离dv(这个距离是只使用一些known顶点作为中间顶点从s到v的最短路径的长),每个顶点还保留字段pv,该字段是记录引起dv变化的最后的顶点。

转载:http://blog.csdn.net/doufei_ccst/article/details/7841311

图顶点信息的数据结构:

[cpp]  view plain  copy
  1. //保存每个顶点信息的数据结构  
  2. struct GraphNode{  
  3.     bool known;//当前顶点距离起点的距离是否确定  
  4.     int dist;//当前顶点到起点的最短距离  
  5.     int path;//当前顶点距离起点的最短路径的前一个顶点  
  6. };  

Dijkstra算法简介:

解决单源最短路径问题的一般方法叫做Dijkstra算法。这个算法是贪心算法最好的例子。
贪心算法一般分阶段求解问题,在每个阶段它都把出现的东西当作是最好的去处理。

与无权最短路径算法一样,Dijkstra算法按阶段进行。在每个阶段,Dijkstra算法选择一个顶点v,它在所有的unknown顶点中具有最小的dv,同时算法声明从s到v的最短路径是known的。其它阶段由顶点dv的更新工作成。

主要思想就是根据已经确定了的点的距离,来确定该点相邻顶点的距离,不断的向外散射,直到所以的点的到起点的最短距离确定为止。

下面是Dijkstra算法具体步骤图示:



       图1 用于测试的有向图
假设源点s=v1,下面是求s到其它点的最短距离。

第一步:初始化顶点信息


第二步:

顶点v1就是顶点s,那么距离顶点v1路径长为0,将顶点v1字段的known设为true。
与s相邻的点为v2,v4。v1到v2的距离为4,v1到v4的距离为1。并更新v2和v4的距离。
p2 = v1; p4 = v1;


第三步:

寻找unknown的顶点中距离最短的顶点,并将该顶点标记为known。所以将v4标记为known。
然后,再根据顶点v4的dv值更新与v4相邻的所有点(v3,v6,v7,v5)的距离。
d4+d43 = 1+2 < d3,所以d3 = 3;p3 = v4;
d4+d46 = 1+8 < d6,所以d6 = 9;p6 = v4;
d4+d47 = 1+4 < d7,所以d7 = 5;p7 = v4;
d4+d45 = 1+2 < d5,所以d5 = 3;p5 = v4;


第四步:

执行第三步,寻找 unknown的顶点中距离最短的顶点,并将该顶点标记为known。 所以将v2标记为known。
然后,再根据顶点v2的dv值更新与v2相邻的所有点(v4,v5)的距离。因为v4已经确定了,就不用更新了。因为d2+d25 = 2 + 10 > d5,d5的值为3。所以不需要更新d5。



第五步:

执行第三步,寻找 unknown的顶点中距离最短的顶点,并将该顶点标记为known。 所以将v5标记为known。
然后,再根据顶点v5的dv值更新与v5相邻的所有点(v7)的距离。因为d5+d57 = 3+6>d7,所以d7不用更新。

第六步:

执行第三步,寻找 unknown的顶点中距离最短的顶点,并将该顶点标记为known。 所以将v3标记为known。
然后,再根据顶点v3的dv值更新与v3相邻的所有点(v1,v6)的距离。因为v1是known的,所以d1不用更新。d3+d36 = 3+5 < d6,所以d6 = d3+d36 = 8; p6 = v3;


第七步:

执行第三步,寻找 unknown的顶点中距离最短的顶点,并将该顶点标记为known。 所以将v7标记为known。
然后,再根据顶点v7的dv值更新与v7相邻的所有点(v6)的距离。因为d7+d76 = 5+1<d6,所以d6=d7+d76 = 6; p6 = v7;



第八步:

执行第三步,寻找 unknown的顶点中距离最短的顶点,并将该顶点标记为known。 所以将v6标记为known。
然后,再根据顶点v6的dv值更新与v6相邻的所有点的距离。结果没有v6指向的其它顶点。


第九步:

执行第三步,寻找 unknown的顶点中距离最短的顶点,并将该顶点标记为known。结果没有找到满足条件的顶点,表明此时已经确定了所有顶点的距离,则退出算法。

Dijkstra算法的基本步骤:

1、初始化顶点信息;v.known = flase;  v.dist = INFINITY;  v.path = 0;
2、对起点s的dist字段设为0;s.dist = 0;
3、从所有顶点中找到dist最小的并且known为false的顶点v。然后将该顶点v的known置为true;
然后更新与顶点v相邻的所有其它known为false的顶点w的dist和path的值。
如果v.dist+distance(v,w) < w.dist;则更新w.dist = v.dist + distance(v, w);w.path=v;
4、循环执行第3步,直到从所有顶点中找不到known为false的顶点v为止,找不到合适的顶点的时候则退出算法。

Dijkstra算法的伪代码:

[plain]  view plain  copy
  1. void Graph::dijkstra(Vertex s)  
  2. {  
  3.           //初始化顶点信息  
  4.           for each Vertex v  
  5.           {  
  6.                     v.known = false;  
  7.                     v.dist = INFINITY;  
  8.                     v.path = 0;  
  9.           }  
  10.            
  11.           //起点s的dist设为0  
  12.           s.dist = 0;  
  13.            
  14.           //循环执行第3步  
  15.           for(; ;)  
  16.           {  
  17.                     //从所有顶点中找到dist最小的并且known为false的顶点v  
  18.                     Vertex v = unknown smallest distance vertex;  
  19.                      
  20.                     //如果没有找到满足条件的v,则退出算法(此时所有顶点已经全部确定了)  
  21.                     if(v == NOT_A_VERTEX)  
  22.                               break;  
  23.                                
  24.                     //将该顶点v的known置为true  
  25.                     v.known = true;  
  26.                      
  27.                     //更新与顶点v相邻的所有其它known为false的顶点w的dist和path的值  
  28.                     for each Vertex w adjacent to v  
  29.                     {  
  30.                               if(!w.known)  
  31.                               {  
  32.                                         //更新w.dist  
  33.                                         if(v.dist+distance(v,w) < w.dist)  
  34.                                         {  
  35.                                                   w.dist = v.dist + distance(v,w);  
  36.                                                   w.path = v;  
  37.                                         }                                              
  38.                               }                          
  39.                     }           
  40.           }  
  41. }  

Dijkstra算法的代码:

[cpp]  view plain  copy
  1. /************************************************* 
  2. *  函数名称:dijkstra(int src) 
  3. *  功能描述:求无权图的任意点到其它顶点的距离 
  4. *  参数列表:src是起点 
  5. *  返回结果:void  
  6. *************************************************/  
  7. void Graph::dijkstra(int src)  
  8. {  
  9.     //初始化顶点信息  
  10.     for(int i = 0; i < vertex_num; ++i){  
  11.         nodeArr[i].known = false;  
  12.         nodeArr[i].dist = INFINITY;  
  13.         nodeArr[i].path = 0;  
  14.     }  
  15.     //重要的一步,开启算法的关键一步  
  16.     nodeArr[src].dist = 0;  
  17.   
  18.     for(; ;){  
  19.         //找到unknown的dist最小的顶点   
  20.         int v = 0;  
  21.         int max = INFINITY;  
  22.         for(int i = 0; i < vertex_num; ++i){  
  23.             if(!nodeArr[i].known && (max > nodeArr[i].dist)){  
  24.                 max = nodeArr[i].dist;  
  25.                 v = i;  
  26.             }  
  27.         }  
  28.   
  29.         //没有找到满足条件的顶点,退出算法  
  30.         if(max == INFINITY)  
  31.             break;  
  32.   
  33.         nodeArr[v].known = true;  
  34.         //更新与v相邻所有顶点w的dist,path  
  35.         for(list<Node>::iterator it = graph_list[v].begin(); it != graph_list[v].end(); ++it){  
  36.             if(!nodeArr[(*it).vertex].known){  
  37.                 if(nodeArr[v].dist + (*it).weight < nodeArr[(*it).vertex].dist){  
  38.                     nodeArr[(*it).vertex].dist = nodeArr[v].dist + (*it).weight;  
  39.                     nodeArr[(*it).vertex].path = v;  
  40.                 }  
  41.             }  
  42.         }  
  43.   
  44.     }  
  45. }  

图类的接口:

[cpp]  view plain  copy
  1. /******************************************************* 
  2. *  类名称: 邻接表图 
  3. ********************************************************/   
  4. class Graph{  
  5.     private:  
  6.         int edge_num;//图边的个数  
  7.         int vertex_num;//图的顶点数目  
  8.         list<Node> * graph_list;//邻接表  
  9.         vector<GraphNode> nodeArr;//保存每个顶点信息的数组  
  10.           
  11.     public:  
  12.         Graph(){}  
  13.         Graph(char* graph[], int edgenum);   
  14.         ~Graph();  
  15.         void print();  
  16.         void dijkstra(int src);  
  17.         void printShorestPath();   
  18.     private:  
  19.         vector<int> get_graph_value(char* graph[], int columns);  
  20.         void addEdge(char* graph[], int columns);  
  21. };  

测试主函数:

[cpp]  view plain  copy
  1. int main(int argc, char *argv[])  
  2. {  
  3.     char *topo[5000];  
  4.     int edge_num;  
  5.     char *demand;  
  6.     int demand_num;  
  7.   
  8.     char *topo_file = argv[1];  
  9.     edge_num = read_file(topo, 5000, topo_file);  
  10.     if (edge_num == 0)  
  11.     {  
  12.         printf("Please input valid topo file.\n");  
  13.         return -1;  
  14.     }  
  15.   
  16.     int src;  
  17.     cout << "输入求最短路径的起点:";  
  18.     cin >> src;  
  19.   
  20.     Graph G(topo, edge_num);  
  21.     G.print();  
  22.       
  23.     cout << "Dijkstra: " << endl;  
  24.     G.dijkstra(src);  
  25.     G.printShorestPath();  
  26.   
  27.   
  28.     release_buff(topo, edge_num);  
  29.   
  30.     return 0;  
  31. }  

测试的图的数据(如下数据以邻接表形式输入“图1 用于测试的有向图”的数据,该数据行12行,每行4列数据,第1列表示边序号、第2列和第3分别为相邻的起止顶点、第4列为边的权值):

[plain]  view plain  copy
  1. 1,1,2,2  
  2. 2,1,4,1  
  3. 3,2,4,3  
  4. 4,2,5,10  
  5. 5,3,1,4  
  6. 6,3,6,5  
  7. 7,4,3,2  
  8. 8,4,6,8  
  9. 9,4,5,2  
  10. 10,4,7,4  
  11. 11,5,7,6  
  12. 12,7,6,1  

图类的源代码:

[cpp]  view plain  copy
  1. #ifndef GRAPH_H  
  2. #define GRAPH_H  
  3.   
  4. #include <list>  
  5. #include <iostream>  
  6. #include <vector>  
  7. #include <stdlib.h>  
  8. #include <string.h>  
  9. #include <algorithm>  
  10. #include <iterator>  
  11. #include <stdio.h>  
  12. #include <errno.h>  
  13. #include <unistd.h>  
  14. #include <signal.h>  
  15. #include <queue>  
  16.   
  17. using namespace std;  
  18.   
  19. #define MAX_VERTEX_NUM 600  
  20. #define INFINITY 1000000//将INFINITY定义为无穷大的值  
  21.   
  22. //保存每个顶点信息的数据结构  
  23. struct GraphNode{  
  24.     bool known;//当前顶点距离起点的距离是否确定  
  25.     int dist;//当前顶点到起点的最短距离  
  26.     int path;//当前顶点距离起点的最短路径的前一个顶点  
  27. };  
  28.   
  29. //图节点信息  
  30. typedef struct Node{   
  31.     int edge_num;//边号   
  32.     int src;//源点   
  33.     int vertex;//自身   
  34.     int weight;//边的权重   
  35. }Node;   
  36.   
  37. /******************************************************* 
  38. *  类名称: 邻接表图 
  39. ********************************************************/   
  40. class Graph{  
  41.     private:  
  42.         int edge_num;//图边的个数  
  43.         int vertex_num;//图的顶点数目  
  44.         list<Node> * graph_list;//邻接表  
  45.         vector<GraphNode> nodeArr;//保存每个顶点信息的数组  
  46.           
  47.     public:  
  48.         Graph(){}  
  49.         Graph(char* graph[], int edgenum);   
  50.         ~Graph();  
  51.         void print();  
  52.         void dijkstra(int src);  
  53.         void printShorestPath();   
  54.     private:  
  55.         vector<int> get_graph_value(char* graph[], int columns);  
  56.         void addEdge(char* graph[], int columns);  
  57. };  
  58.   
  59.   
  60. /************************************************* 
  61. *  函数名称:dijkstra(int src) 
  62. *  功能描述:求无权图的任意点到其它顶点的距离 
  63. *  参数列表:src是起点 
  64. *  返回结果:void  
  65. *************************************************/  
  66. void Graph::dijkstra(int src)  
  67. {  
  68.     //初始化顶点信息  
  69.     for(int i = 0; i < vertex_num; ++i){  
  70.         nodeArr[i].known = false;  
  71.         nodeArr[i].dist = INFINITY;  
  72.         nodeArr[i].path = 0;  
  73.     }  
  74.     //重要的一步,开启算法的关键一步  
  75.     nodeArr[src].dist = 0;  
  76.   
  77.     for(; ;){  
  78.         //找到unknown的dist最小的顶点   
  79.         int v = 0;  
  80.         int max = INFINITY;  
  81.         for(int i = 0; i < vertex_num; ++i){  
  82.             if(!nodeArr[i].known && (max > nodeArr[i].dist)){  
  83.                 max = nodeArr[i].dist;  
  84.                 v = i;  
  85.             }  
  86.         }  
  87.   
  88.         //没有找到满足条件的顶点,退出算法  
  89.         if(max == INFINITY)  
  90.             break;  
  91.   
  92.         nodeArr[v].known = true;  
  93.         //更新与v相邻所有顶点w的dist,path  
  94.         for(list<Node>::iterator it = graph_list[v].begin(); it != graph_list[v].end(); ++it){  
  95.             if(!nodeArr[(*it).vertex].known){  
  96.                 if(nodeArr[v].dist + (*it).weight < nodeArr[(*it).vertex].dist){  
  97.                     nodeArr[(*it).vertex].dist = nodeArr[v].dist + (*it).weight;  
  98.                     nodeArr[(*it).vertex].path = v;  
  99.                 }  
  100.             }  
  101.         }  
  102.   
  103.     }  
  104. }  
  105.   
  106. /************************************************* 
  107. *  函数名称:printShorestPath() 
  108. *  功能描述:将获得的src顶点到其它顶点的最短路径输出 
  109. *  参数列表:无 
  110. *  返回结果:无 
  111. *************************************************/  
  112. void Graph::printShorestPath()  
  113. {  
  114.     cout << "顶点\t" << "known\t" << "dist\t" << "path" << endl;  
  115.     for(int i = 0; i < vertex_num; ++i){  
  116.         if(nodeArr[i].known)  
  117.             cout << i << "\t" << nodeArr[i].known << "\t" << nodeArr[i].dist << "\t" << nodeArr[i].path << endl;  
  118.     }   
  119. }  
  120.   
  121. /************************************************* 
  122. *  函数名称:print 
  123. *  功能描述:将图的信息以邻接表的形式输出到标准输出 
  124. *  参数列表:无 
  125. *  返回结果:无 
  126. *************************************************/  
  127. void Graph::print()  
  128. {  
  129.     cout << "******************************************************************" << endl;   
  130.     //for(int i = 0 ; i < MAX_VERTEX_NUM; ++i){  
  131.     for(int i = 0 ; i < vertex_num; ++i){  
  132.         if(graph_list[i].begin() != graph_list[i].end()){  
  133.             cout << i << "-->";  
  134.             for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){  
  135.                 cout << (*it).vertex << "(边号:" << (*it).edge_num << ",权重:" << (*it).weight << ")-->";  
  136.             }  
  137.             cout << "NULL" << endl;  
  138.         }  
  139.     }  
  140.   
  141.     cout << "******************************************************************" << endl;   
  142. }  
  143.   
  144. /************************************************* 
  145. *  函数名称:get_graph_value 
  146. *  功能描述:将图的每一条边的信息保存到一个数组中 
  147. *  参数列表: graph:指向图信息的二维数组 
  148.              columns:图的第几条边 
  149. *  返回结果:无 
  150. *************************************************/  
  151. vector<int> Graph::get_graph_value(char* graph[], int columns)  
  152. {  
  153.     vector<int> v;  
  154.     char buff[20];  
  155.     int i = 0, j = 0, val;  
  156.     memset(buff, 0, 20);  
  157.   
  158.     while((graph[columns][i] != '\n') && (graph[columns][i] != '\0')){  
  159.         if(graph[columns][i] != ','){  
  160.             buff[j] = graph[columns][i];  
  161.             j++;  
  162.         }  
  163.         else{  
  164.             j = 0;  
  165.             val = atoi(buff);   
  166.             v.push_back(val);  
  167.             memset(buff, 0, 20);  
  168.         }  
  169.         i++;  
  170.     }  
  171.     val = atoi(buff);   
  172.     v.push_back(val);  
  173.   
  174.     return v;  
  175. }  
  176.   
  177.   
  178.   
  179. /************************************************* 
  180. *  函数名称:addEdge 
  181. *  功能描述:将图的每一条边的信息加入图的邻接表中 
  182. *  参数列表:graph:指向图信息的二维数组 
  183.              columns:图的第几条边 
  184. *  返回结果:无 
  185. *************************************************/  
  186. void Graph::addEdge(char* graph[], int columns)  
  187. {  
  188.     Node node;  
  189.     vector<int> v = get_graph_value(graph, columns);  
  190.   
  191.     node.edge_num = v[0];  
  192.     node.src = v[1];  
  193.     node.vertex = v[2];  
  194.     node.weight = v[3];  
  195.   
  196.   
  197.     //根据顶点的标号,求的总的顶点数目  
  198.     if(node.vertex > vertex_num)  
  199.         vertex_num = node.vertex;  
  200.   
  201.     //要考虑重复的边,但是边的权重不一样  
  202.     for(list<Node>::iterator it = graph_list[node.src].begin(); it != graph_list[node.src].end(); ++it){  
  203.         if((*it).vertex == node.vertex){  
  204.             if((*it).weight > node.weight){  
  205.                 (*it).weight = node.weight;     
  206.             }  
  207.             return;  
  208.         }  
  209.     }  
  210.   
  211.     graph_list[node.src].push_back(node);  
  212. }  
  213.   
  214.   
  215. /************************************************* 
  216. *  函数名称:构造函数 
  217. *  功能描述:以邻接表的形式保存图的信息,并保存必须经过的顶点 
  218. *  参数列表:graph:指向图信息的二维数组 
  219.              edgenum:图的边的个数 
  220. *  返回结果:无 
  221. *************************************************/  
  222. Graph::Graph(char* graph[], int edgenum):nodeArr(MAX_VERTEX_NUM)  
  223. {  
  224.     edge_num =  edgenum;   
  225.     vertex_num = 0;  
  226.     graph_list = new list<Node>[MAX_VERTEX_NUM+1];  
  227.   
  228.   
  229.     for(int i = 0; i < edgenum; ++i){  
  230.         addEdge(graph, i);     
  231.     }  
  232.   
  233.     //对顶点信息进行初始化  
  234.     for(int i = 0; i < MAX_VERTEX_NUM; ++i){  
  235.         nodeArr[i].known = false;  
  236.         nodeArr[i].dist = INFINITY;  
  237.         nodeArr[i].path = -1;//如果为-1,表示没有该点,配合dijkstra算法使用  
  238.     }  
  239.   
  240.     vertex_num++;  
  241. }  
  242.   
  243.   
  244. /************************************************* 
  245. *  函数名称:析构函数 
  246. *  功能描述:释放动态分配的内存 
  247. *  参数列表:无 
  248. *  返回结果:无 
  249. *************************************************/  
  250. Graph::~Graph()  
  251. {  
  252.     delete[] graph_list;  
  253. }  
  254.   
  255. #endif  

测试函数的源代码:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <assert.h>  
  5. #include <time.h>  
  6. #include <sys/timeb.h>  
  7. #include <errno.h>  
  8. #include <unistd.h>  
  9. #include <signal.h>  
  10. #include <stdio.h>  
  11. #include "graphDijkstra.h"  
  12.   
  13. #define MAX_LINE_LEN 4000  
  14.   
  15. int read_file(char ** const buff, const unsigned int spec, const char * const filename);  
  16. void release_buff(char ** const buff, const int valid_item_num);  
  17.   
  18. int main(int argc, char *argv[])  
  19. {  
  20.     char *topo[5000];  
  21.     int edge_num;  
  22.     char *demand;  
  23.     int demand_num;  
  24.   
  25.     char *topo_file = argv[1];  
  26.     edge_num = read_file(topo, 5000, topo_file);  
  27.     if (edge_num == 0)  
  28.     {  
  29.         printf("Please input valid topo file.\n");  
  30.         return -1;  
  31.     }  
  32.   
  33.     int src;  
  34.     cout << "输入求最短路径的起点:";  
  35.     cin >> src;  
  36.   
  37.     Graph G(topo, edge_num);  
  38.     G.print();  
  39.       
  40.     cout << "Dijkstra: " << endl;  
  41.     G.dijkstra(src);  
  42.     G.printShorestPath();  
  43.   
  44.   
  45.     release_buff(topo, edge_num);  
  46.   
  47.     return 0;  
  48. }  
  49.   
  50. /**************************************************************** 
  51. *   函数名称:read_file 
  52. *   功能描述: 读取文件中的图的数据信息 
  53. *   参数列表: buff是将文件读取的图信息保存到buff指向的二维数组中  
  54. *             spec是文件中图最大允许的边的个数 
  55. *             filename是要打开的图文件 
  56. *   返回结果:无 
  57. *****************************************************************/  
  58. int read_file(char ** const buff, const unsigned int spec, const char * const filename)  
  59. {  
  60.     FILE *fp = fopen(filename, "r");  
  61.     if (fp == NULL)  
  62.     {  
  63.         printf("Fail to open file %s, %s.\n", filename, strerror(errno));  
  64.         return 0;  
  65.     }  
  66.     printf("Open file %s OK.\n", filename);  
  67.   
  68.     char line[MAX_LINE_LEN + 2];  
  69.     unsigned int cnt = 0;  
  70.     while ((cnt < spec) && !feof(fp))  
  71.     {  
  72.         line[0] = 0;  
  73.         fgets(line, MAX_LINE_LEN + 2, fp);  
  74.         if (line[0] == 0)   continue;  
  75.         buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2);  
  76.         strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1);  
  77.         buff[cnt][4001] = 0;  
  78.         cnt++;  
  79.     }  
  80.     fclose(fp);  
  81.     printf("There are %d lines in file %s.\n", cnt, filename);  
  82.   
  83.     return cnt;  
  84. }  
  85.   
  86. /**************************************************************** 
  87. *   函数名称:release_buff 
  88. *   功能描述: 释放刚才读取的文件中的图的数据信息 
  89. *   参数列表: buff是指向文件读取的图信息 
  90. *             valid_item_num是指图中边的个数 
  91. *   返回结果:void 
  92. *****************************************************************/  
  93. void release_buff(char ** const buff, const int valid_item_num)  
  94. {  
  95.     for (int i = 0; i < valid_item_num; i++)  
  96.         free(buff[i]);  
  97. }  

运行结果:

猜你喜欢

转载自blog.csdn.net/zhouxianen1987/article/details/78845100