侵删https://blog.csdn.net/qq_35644234/article/details/60870719

前言

Nobody can go back and start a new beginning,but anyone can start today and make a new ending.

Name:Willam

Time:2017/3/8

1、最短路径问题介绍

问题解释:

从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径

解决问题的算法:

这篇博客,我们就对Dijkstra算法来做一个详细的介绍

2、Dijkstra算法介绍

  • 算法特点:

    迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

  • 算法的思路

    Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。

    然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点,

    然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。

    然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。

3、Dijkstra算法示例演示

下面我求下图,从顶点v1到其他各个顶点的最短路径

首先第一步,我们先声明一个dis数组,该数组初始化的值为:

我们的顶点集T的初始化为:T={v1}

既然是求 v1顶点到其余各个顶点的最短路程,那就先找一个离 1 号顶点最近的顶点。通过数组 dis 可知当前离v1顶点最近是 v3顶点。当选择了 2 号顶点后,dis[2](下标从0开始)的值就已经从“估计值”变为了“确定值”,即 v1顶点到 v3顶点的最短路程就是当前 dis[2]值。将V3加入到T中。

为什么呢?因为目前离 v1顶点最近的是 v3顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 v1顶点到 v3顶点的路程进一步缩短了。因为 v1顶点到其它顶点的路程肯定没有 v1到 v3顶点短.

OK,既然确定了一个顶点的最短路径,下面我们就要根据这个新入的顶点V3会有出度,发现以v3 为弧尾的有: < v3,v4 >,那么我们看看路径:v1–v3–v4的长度是否比v1–v4短,其实这个已经是很明显的了,因为dis[3]代表的就是v1–v4的长度为无穷大,而v1–v3–v4的长度为:10+50=60,所以更新dis[3]的值,得到如下结果:

因此 dis[3]要更新为 60。这个过程有个专业术语叫做“松弛”。即 v1顶点到 v4顶点的路程即 dis[3],通过 < v3,v4> 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛v1顶点到其余各个顶点的路程。

然后,我们又从除dis[2]和dis[0]外的其他值中寻找最小值,发现dis[4]的值最小,通过之前是解释的原理,可以知道v1到v5的最短距离就是dis[4]的值,然后,我们把v5加入到集合T中,然后,考虑v5的出度是否会影响我们的数组dis的值,v5有两条出度:< v5,v4>和 < v5,v6>,然后我们发现:v1–v5–v4的长度为:50,而dis[3]的值为60,所以我们要更新dis[3]的值.另外,v1-v5-v6的长度为:90,而dis[5]为100,所以我们需要更新dis[5]的值。更新后的dis数组如下图:

然后,继续从dis中选择未确定的顶点的值中选择一个最小的值,发现dis[3]的值是最小的,所以把v4加入到集合T中,此时集合T={v1,v3,v5,v4},然后,考虑v4的出度是否会影响我们的数组dis的值,v4有一条出度:< v4,v6>,然后我们发现:v1–v5–v4–v6的长度为:60,而dis[5]的值为90,所以我们要更新dis[5]的值,更新后的dis数组如下图:

然后,我们使用同样原理,分别确定了v6和v2的最短路径,最后dis的数组的值如下:

因此,从图中,我们可以发现v1-v2的值为:∞,代表没有路径从v1到达v2。所以我们得到的最后的结果为:

  1. 起点 终点 最短路径 长度
  2. v1 v2
  3. v3 {v1,v3} 10
  4. v4 {v1,v5,v4} 50
  5. v5 {v1,v5} 30
  6. v6 {v1v5,v4,v6} 60
  7.  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

4、Dijkstra算法的代码实现(c++)

  • Dijkstra.h文件的代码
  1. /************************************************************/
  2. /* 程序作者:Willam */
  3. /* 程序完成时间:2017/3/8 */
  4. /* 有任何问题请联系:2930526477@qq.com */
  5. /************************************************************/
  6. //@尽量写出完美的程序

#pragma once
//#pragma once是一个比较常用的C/C++杂注,
//只要在头文件的最开始加入这条杂注,
//就能够保证头文件只被编译一次。

#include<iostream>

include<string>

using namespace std;

/*

本程序是使用Dijkstra算法实现求解最短路径的问题

采用的邻接矩阵来存储图

*/

//记录起点到每个顶点的最短路径的信息

struct Dis {

string path;

int value;

bool visit;

Dis() {

visit = false;

value = 0;

path = “”;

}

};

class Graph_DG {

private:

int vexnum; //图的顶点个数

int edge; //图的边数

int **arc; //邻接矩阵

Dis * dis; //记录各个顶点最短路径的信息

public:

//构造函数

Graph_DG(int vexnum, int edge);

//析构函数

~Graph_DG();

// 判断我们每次输入的的边的信息是否合法

//顶点从1开始编号

bool check_edge_value(int start, int end, int weight);

//创建图

void createGraph();

//打印邻接矩阵

void print();

//求最短路径

void Dijkstra(int begin);

//打印最短路径

void print_path(int);

};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • Dijkstra.cpp文件的代码
  1. #include"Dijkstra.h"
  2. //构造函数
  3. Graph_DG::Graph_DG(int vexnum, int edge) {
  4. //初始化顶点数和边数
  5. this->vexnum = vexnum;
  6. this->edge = edge;
  7. //为邻接矩阵开辟空间和赋初值
  8. arc = new int*[this->vexnum];
  9. dis = new Dis[this->vexnum];
  10. for (int i = 0; i < this->vexnum; i++) {
  11. arc[i] = new int[this->vexnum];
  12. for (int k = 0; k < this->vexnum; k++) {
  13. //邻接矩阵初始化为无穷大
  14. arc[i][k] = INT_MAX;
  15. }
  16. }
  17. }
  18. //析构函数
  19. Graph_DG::~Graph_DG() {
  20. delete[] dis;
  21. for (int i = 0; i < this->vexnum; i++) {
  22. delete this->arc[i];
  23. }
  24. delete arc;
  25. }
  26. // 判断我们每次输入的的边的信息是否合法
  27. //顶点从1开始编号
  28. bool Graph_DG::check_edge_value(int start, int end, int weight) {
  29. if (start<1 || end<1 || start>vexnum || end>vexnum || weight < 0) {
  30. return false;
  31. }
  32. return true;
  33. }
  34. void Graph_DG::createGraph() {
  35. cout << "请输入每条边的起点和终点(顶点编号从1开始)以及其权重" << endl;
  36. int start;
  37. int end;
  38. int weight;
  39. int count = 0;
  40. while (count != this->edge) {
  41. cin >> start >> end >> weight;
  42. //首先判断边的信息是否合法
  43. while (!this->check_edge_value(start, end, weight)) {
  44. cout << "输入的边的信息不合法,请重新输入" << endl;
  45. cin >> start >> end >> weight;
  46. }
  47. //对邻接矩阵对应上的点赋值
  48. arc[start - 1][end - 1] = weight;
  49. //无向图添加上这行代码
  50. //arc[end - 1][start - 1] = weight;
  51. ++count;
  52. }
  53. }
  54. void Graph_DG::print() {
  55. cout << "图的邻接矩阵为:" << endl;
  56. int count_row = 0; //打印行的标签
  57. int count_col = 0; //打印列的标签
  58. //开始打印
  59. while (count_row != this->vexnum) {
  60. count_col = 0;
  61. while (count_col != this->vexnum) {
  62. if (arc[count_row][count_col] == INT_MAX)
  63. cout << "∞" << " ";
  64. else
  65. cout << arc[count_row][count_col] << " ";
  66. ++count_col;
  67. }
  68. cout << endl;
  69. ++count_row;
  70. }
  71. }
  72. void Graph_DG::Dijkstra(int begin){
  73. //首先初始化我们的dis数组
  74. int i;
  75. for (i = 0; i < this->vexnum; i++) {
  76. //设置当前的路径
  77. dis[i].path = "v" + to_string(begin) + "-->v" + to_string(i + 1);
  78. dis[i].value = arc[begin - 1][i];
  79. }
  80. //设置起点的到起点的路径为0
  81. dis[begin - 1].value = 0;
  82. dis[begin - 1].visit = true;
  83. int count = 1;
  84. //计算剩余的顶点的最短路径(剩余this->vexnum-1个顶点)
  85. while (count != this->vexnum) {
  86. //temp用于保存当前dis数组中最小的那个下标
  87. //min记录的当前的最小值
  88. int temp=0;
  89. int min = INT_MAX;
  90. for (i = 0; i < this->vexnum; i++) {
  91. if (!dis[i].visit && dis[i].value<min) {
  92. min = dis[i].value;
  93. temp = i;
  94. }
  95. }
  96. //cout << temp + 1 << " "<<min << endl;
  97. //把temp对应的顶点加入到已经找到的最短路径的集合中
  98. dis[temp].visit = true;
  99. ++count;
  100. for (i = 0; i < this->vexnum; i++) {
  101. //注意这里的条件arc[temp][i]!=INT_MAX必须加,不然会出现溢出,从而造成程序异常
  102. if (!dis[i].visit && arc[temp][i]!=INT_MAX && (dis[temp].value + arc[temp][i]) < dis[i].value) {
  103. //如果新得到的边可以影响其他为访问的顶点,那就就更新它的最短路径和长度
  104. dis[i].value = dis[temp].value + arc[temp][i];
  105. dis[i].path = dis[temp].path + "-->v" + to_string(i + 1);
  106. }
  107. }
  108. }
  109. }
  110. void Graph_DG::print_path(int begin) {
  111. string str;
  112. str = "v" + to_string(begin);
  113. cout << "以"<<str<<"为起点的图的最短路径为:" << endl;
  114. for (int i = 0; i != this->vexnum; i++) {
  115. if(dis[i].value!=INT_MAX)
  116. cout << dis[i].path << "=" << dis[i].value << endl;
  117. else {
  118. cout << dis[i].path << "是无最短路径的" << endl;
  119. }
  120. }
  121. }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
  • main.cpp文件的代码
  1. #include"Dijkstra.h"
  2. //检验输入边数和顶点数的值是否有效,可以自己推算为啥:
  3. //顶点数和边数的关系是:((Vexnum*(Vexnum - 1)) / 2) < edge
  4. bool check(int Vexnum, int edge) {
  5. if (Vexnum <= 0 || edge <= 0 || ((Vexnum*(Vexnum - 1)) / 2) < edge)
  6. return false;
  7. return true;
  8. }
  9. int main() {
  10. int vexnum; int edge;
  11. cout << "输入图的顶点个数和边的条数:" << endl;
  12. cin >> vexnum >> edge;
  13. while (!check(vexnum, edge)) {
  14. cout << "输入的数值不合法,请重新输入" << endl;
  15. cin >> vexnum >> edge;
  16. }
  17. Graph_DG graph(vexnum, edge);
  18. graph.createGraph();
  19. graph.print();
  20. graph.Dijkstra(1);
  21. graph.print_path(1);
  22. system("pause");
  23. return 0;
  24. }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

输入:

  1. 6 8
  2. 1 3 10
  3. 1 5 30
  4. 1 6 100
  5. 2 3 5
  6. 3 4 50
  7. 4 6 10
  8. 5 6 60
  9. 5 4 20
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

输出:

从输出可以看出,程序的结果和我们之前手动计算的结果是一样的。

最短路径问题---Dijkstra算法详解的更多相关文章

  1. 图的最短路径-----------Dijkstra算法详解(TjuOj2870_The Kth City)

    做OJ需要用到搜索最短路径的题,于是整理了一下关于图的搜索算法: 图的搜索大致有三种比较常用的算法: 迪杰斯特拉算法(Dijkstra算法) 弗洛伊德算法(Floyd算法) SPFA算法 Dijkst ...

  2. Dijkstra算法详解

    1.dijkstra算法简介 Dijkstra算法是由E.W.Dijkstra于1959年提出,又叫迪杰斯特拉算法,它应用了贪心算法模式,是目前公认的最好的求解最短路径的方法.算法解决的是有向图中单个 ...

  3. HDU 1874 畅通工程续-- Dijkstra算法详解 单源点最短路问题

    参考 此题Dijkstra算法,一次AC.这个算法时间复杂度O(n2)附上该算法的演示图(来自维基百科): 附上:  迪科斯彻算法分解(优酷) problem link -> HDU 1874 ...

  4. 【最短路径Floyd算法详解推导过程】看完这篇,你还能不懂Floyd算法?还不会?

    简介 Floyd-Warshall算法(Floyd-Warshall algorithm),是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似.该算法名称以 ...

  5. HDU1548——A strange lift(最短路径:dijkstra算法)

    A strange lift DescriptionThere is a strange lift.The lift can stop can at every floor as you want, ...

  6. BM算法  Boyer-Moore高质量实现代码详解与算法详解

    Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...

  7. kmp算法详解

    转自:http://blog.csdn.net/ddupd/article/details/19899263 KMP算法详解 KMP算法简介: KMP算法是一种高效的字符串匹配算法,关于字符串匹配最简 ...

  8. 机器学习经典算法详解及Python实现--基于SMO的SVM分类器

    原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector  ...

  9. [转] KMP算法详解

    转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段.    我们这里说的K ...

随机推荐

  1. Java类锁和对象锁

    一.类锁和对象锁 二.使用注意 三.参考资料 一.类锁和对象锁 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class) 对象锁:在代码 ...

  2. 【Shell】30分钟关闭Tcpdump,开启Tcpdump、检测目录大小终止任务

    场景 按照一定时间规律运行Tcpdump 思路 编程思路细化思考 查看文件个数 file_count_results=`ls -al "C:\\Users\\Windows32\\Deskt ...

  3. dubbo源码分析9——ServiceBean的afterPropertiesSet方法分析

    ServiceBean的afterPropertiesSet方法是实现了InitializingBean,还是准备先做宏观分析,然后再做细致分析.下面先宏观分析:  public void after ...

  4. dubbo源码分析3——SPI机制中的ExtensionLoader类的objectFactory属性分析

    ExtensionLoader类是整个SPI的核心类,每个SPI都会对应一个ExtensionLoader类实例,这个类的构造方法如下: private ExtensionLoader(Class&l ...

  5. Memcache的安装和使用【转】

    转自:https://www.cnblogs.com/caoxiaojian/p/5715573.html Memcache是高性能,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问 ...

  6. Python运维开发基础04-语法基础【转】

    上节作业回顾(讲解+温习90分钟) #!/usr/bin/env python3 # -*- coding:utf-8 -*- # author:Mr.chen # 仅用列表+循环实现“简单的购物车程 ...

  7. <第一站>人生的第一个博客

    在畅畅的疯狂暗示下(“最好”建个博客),我决定在博客园开通我的博客,在此记入我从3月23起的所学所想.在他的提醒之前,我曾经断断续续的在日记本上,手机备忘录里记录过我的学习情况和心路历程,总的来说,自 ...

  8. Kafka中文官方文档

    参见链接:http://orchome.com/kafka/index

  9. Webapi 跨域 解决解决错误No 'Access-Control-Allow-Origin' header is present on the requested resource 问题

    首先是web端(http://localhost:53784) 请求 api(http://localhost:81/api/)时出现错误信息: 查看控制台会发现错误:XMLHttpRequest c ...

  10. Linux 的 OOM 终结者(Out Of Memory killer)

    现在是早晨6点钟.已经醒来的我正在总结到底是什么事情使得我的起床闹铃提前了这么多.故事刚开始的时候,手机铃声恰好停止.又困又烦躁的我看了下手机,看看是不是我自己疯了把闹钟调得这么早,居然是早晨5点.然 ...