这个算法可是属于真正的老经典算法了,这学期离散数学里学到的唯一一个算法也就是这个Dijkstra算法,这个算法实际上就有贪心算法的味道在里面,即每次都确定一个顶点的最优路径,直到遍历全图。由于本人水平真的过低,这学期在离散数学中学这个算法的时候真的是花了很多很多时间才弄明白算法原理,曾一度怀疑人生并且无数次想要撕碎这个课本。我真的搞不懂明明很简单的一个算法,为什么放到教科书上面会给花里胡哨地加上一些专有名词,然后变得极度抽象,我是真的是很无语。。。个人感觉我们学校订的教科书编的不是很好(纯粹个人感觉。)

这边我尽量用最简洁易懂的方式,将这个算法表示清楚。因为我太清楚深受教科书毒害的学生们的感受了。。

首先,我们要明白,这个算法到底有什么用?(学一个新东西,要么是知道它有什么用(功利化学习),要么就是很喜欢它,就是想学(去功利化),当然如果你能做到二者兼顾,那最好了)这个算法,是求最短路径的,也就是求一个点到另一个点的最短路径,这里的最短路径指的是权值大于等于0的路,为什么是这样,这个我会在后文说到。至于为什么是带权,是为了与BFS广度优先搜索算法区分,BFS也是求最短路径,但是求的是不带权的路,也就是段数最少的路径。

在这之后,我们就可以开始学习这个算法了,这个算法的核心思想完全可以用一句话概括,“每次都确定初始点到一个顶点的最短路径,直到遍历全部顶点”。因为如果直接阐述思想,会过于抽象,难以理解,因此我这边就打算结合例子解释。这是我从书上找到的一个比较好的例子,这个例子弄懂了基本上这个算法也就掌握了。

首先,我们要明确我们的目标,我们要用Dijkstra算法算出b点到图上所有点的最短路径。在明确了目标之后,我们要先定义一个map<char,int> cost,存储从初始点点到各点的当前最短距离(当然这里不一定要用map,思想一样就可以,我这里用map是为了更好地理解),然后就是让计算机存储图的信息,这里我选择用map<char,map<char,int> > graph,(是不是感觉这里有点复杂。。其实完全不是的,这里完全就可以看成一个二维数组,a[x][y],里面存储的值是x到y的距离),再然后就是我们需要再定义一个map<char,char> parents,存储最短路径上一个顶点的前一个顶点,也就是存储父节点,用来进行回溯,这样才能把最短路径经过的节点给一一求出来(如果你不需要求最短路径经过的节点,这个就不需要定义了,当然,最好还是要求出来,这样才能让这个算法更加elegant)。最后就是定义一个vector< char > over,用来存储已经确定好最短路径的顶点(这是Dijkstra算法思想中最关键的一环,就是你每次确定好的那个最短路径的顶点,它的最短路径到程序结束都不会再改变了,因此将其放到vector中,避免再对它进行操作),做好了上述三步,就可以开始我们的算法了。

根据上述三步,我们一开始确定的cost散列表(第一列是节点,第二列是初始点到各个节点的当前最短距离),如下·
这里切记,一开始我们只能确定b点到b点的距离是0(这是由于程序编写的需要),其他点默认正无穷。 而我们每一次都需要在cost里面找到一个最短的距离,通过这个最短距离确定一个节点。然后这就算确定了一个顶点的最短路径,也就是确定了b点到b点的最短路径,将其加入到over容器中,之后就不再需要求b点到b点的最短路径了(这个是不是在我们人看起来很傻。。其实我们的目的是让计算机识图,因此要把计算机当成一个蠢的不能再蠢的人看待

之后我们就要开始更新这个cost散列表了,这也是Dijkstra算法很关键的一步,就是由于我们已经确定了到b点的最短路径,因此我们可以搜索b点到各个直接相邻的点的权值(也就是相邻边的长度),让已经确定的cost中b点的最短路径径加上权值,就能求出来b点到各个相邻点的距离(记住,这里是一个距离,不是最短距离,如果要是最短距离,一定一定要比较才能得出),此时我们将这个距离和我们存储在cost散列表里的当前最短距离比较,如果这个距离比当前最短距离小,就进行更新,将cost里面对应位置的值变成这个距离,然后把b设定为这个节点的父节点。在更新完后,我们的cost散列表就变成了这样,

b节点后面加上*就表示它是已经确定的节点了(也就是已经求出了初始点到这个节点的最短路径了),之后再在cost里面找,到一个顶点的最短路径,就不再需要考虑它了,切记切记!

之后就非常简单了,就是重复上面的步骤。再从cost里面找cost里面最小的值代表的节点(除已经确定的节点以外),找出这个节点,然后重复上面步骤就可以。这次我们找出的节点是c节点,然后cost散列表更新后就变成,

之后就是重复到所有顶点后面都加上*后,这个算法就算结束了,然后cost最终存储的值就是初始点到各个点的最短路径,若想具体求出,通过parents回溯即可。

这里我给出参考书上的解析图,

这里面每一行代表一次计算,每一次计算确立一个顶点,找到一条最短路径,然后括号的第一个参数是初始点到这个点的当前最短距离,也就是cost存储的值,然后第二个参数就是存储这个节点的父节点,λ表示暂未确定父节点。

最终cost的结果如下,

具体路径如下,

是不是看到这里感觉这个算法难度还好,不是很难。但是这个算法有几个很有想法的点,如果你有真正的去思考这个算法,你应该可以感受到。一,为什么每次确定下来的那个节点,之后就不需要再需要对它进行更新了?二,为什么就能很肯定每次确定下来的节点它对应的cost值就是最终的最短路径?三,为什么每次只能确定一个节点?四,为什么这个算法无法解决有负权边的情况?五,为什么要根据每次确定的节点对cost进行更新?想清楚了这几个问题,你才能能说真正的懂这个算法,要不然永远只能根据框架去套它,一辈子在门外徘徊。

首先,前四个问题其实是统一的,你只要弄清楚其中任何一个,其他几个就豁然开朗了。我这边就从第一个问题切入,首先要明白我们每次计算是如何确定那一个节点的?是在cost当前最短路径中找到目前最快能到达的节点(也就是目前初始节点到这个节点的距离最短),而当我们找到了这个节点,我们就当在cost中存储的值是初始节点到这个节点的最短路径,后续就不用再对这个节点在cost中的值进行更新了。这是为什么?

就是因为我们伟大的Dijkstra算法只应用于权值大于等于0的图,回到这个问题的一开始,我们每次要确定的节点是在cost当前最短路径中找到目前最快能到达的节点,如果我之后又能找到其他到这个节点的路径,那么我能找到的到这个节点的除cost中的最短路径的路径必然是cost中的未确定节点中除这个节点以外的节点对应的值再加上一个连接这俩个节点的边的权值(为什么这里说是cost中的未确定的节点,因为未确定的节点已经更新过了,我们不用再管它,若是你读了后面问题五的解释,你再回过头来仔细想想,就能明白,其实经过那些已经确定的节点的最短路径加上一条连接这俩个节点的边得到的值我们已经求过了!),而我们这次确定的节点中cost的值已经是除未确定节点中最小的了,其他节点再加上权值大于等于0的边,必然会比这个值大的!这里我要举个例子,因为这个是比较难理解的点,空讲的话就会有点抽象。


这里我定义的初始节点是家,根据算法,我第一次确定的节点是家,然后第二次确定的节点就是学校,也就是说到学校的最短路径是2,那有没有可能将到学校的路径缩短到少于2呢?答案是完全不可能,因为你到除家以外的其他节点已经要花比到这个节点更长的距离了,再加上大于等于0的权边,就完全不可能实现了,因此我上面说的一大段话就是说明这个道理。

至于最后的第五个问题,为什么要根据每次确定的节点对cost进行更新,这是因为,一个节点的最短路径必然是与它相邻节点的最短路径加上相邻边之后得到的。我们确定的这个节点是我们已经知道了它的最短路径,然后我们根据这个最短路径进行扩展,试图根据这个最短路径扩展到其他节点,得到到其他节点的路径(但是这个不一定是最短路径,这是很容易犯的错误,我之前也懵逼了很久,因为原先cost里面存的那个值也是与它相邻节点的最短路径加上相邻边之后得到的),根据上面我对问题一的解释,我们每次最终确定下来的节点它对应的cost值就是最终的最短路径,然后我们在确定了一个节点之后就需要确定下一个节点,直到所有节点都被确定,

那么,我们就需要时刻保持cost中存储的是初始节点到各个节点的当前最短路径,而每当我们确定下一个节点之后,我们就可以利用与这个节点相邻的边的信息,从而得到如上面所说的到其他节点的路径长度,而将其求出来的路径长度和我们cost里面存储的路径长度比较,更短的那个就是当前最短路径。

因此,从这里可以看出,根据每次确定的节点对cost进行更新的很大一部分原因就是在合理利用所有边的信息的情况下,时刻保持cost中存储的是初始节点到各个节点的当前最短路径。因为当前最短路径只有俩种可能,一是根据上一个已经确定的节点进行扩展得到的路径,二是之前已经存储到cost中的当前最短路径(这个是根据上一个之前已经确定的节点进行扩展得到的路径)。(一个节点的最短路径必然是它与相邻节点的最短路径加上相邻边之后得到的,这个是最最最最重要的!

如果不是根据确定的节点对cost进行更新的话,那么我们就会将第一个情况变成根据未确定的节点进行扩展得到的路径,而到未确定的节点的最短路径我们还没有求出来,也就是得到了一条很有可能不是由最短路径加上相邻边得到的路径,这就会导致算法出错了,使得我们有可能漏掉更短的最短路径,从而有可能让cost散列表没有处于“时刻存储的是当前最短路径”的情况,最终让算法执行失败。

额,可能是我的水平实在过低,我原本的想法是尽可能简洁易懂,但是一不小心还是打了这么多字才把我认为核心的地方讲完,可能在某些人看来是啰里啰唆搞了半天吧。。。不过我还是很开心的,因为我在解释这五个问题的同时,也对我之前学习的知识进行重塑,也让我对这个算法有了进一步的认识,很多之前没有想清楚的地方也都豁然开朗了,果然,学无止境这个成语真的很有道理。。。

真正弄懂了这个算法的思想之后,剩下的就是相对比较简单的代码实现了。我这边就直接给出代码,

#include<bits/stdc++.h>
using namespace std;
int n, m;
int find_lowest_cost(map<int, int> cost, vector<int>& over)//找到当前开销最小的节点(不包括over中的节点)
{int low;for (int i = 1; i <= n; i++){if (find(over.begin(), over.end(), i) == over.end()) low = i;}for (int i = 1; i <= n; i++){if (cost[i] < cost[low] && (find(over.begin(), over.end(), i) == over.end())) low = i;}return low;
}
int main()
{int x, y, w, low;long long sum = 0;map<int, map<int, int> > graph;map<int, int>cost;map<int, int>parents;vector<int>over;while (cin >> n >> m){while (m--)  //输入图的信息{cin >> x >> y >> w;if (graph[x][y] > 0){if (graph[x][y] > w) graph[x][y] = w;}else  graph[x][y] = w;}cost[1] = 0;for (int i = 2; i <= n; i++) cost[i] = 10001; //10001是因我我假设最大边为10000,设为10001就表示无法直接到达这个节点low = 1;over.push_back(low); for (int i = 1; i <= n; i++)  //每次循环代表一次更新,以及找到一个节点的最终的最短路径{for (int i = 1; i <= n; i++)    //更新cost{if ((graph[low][i]>0) && (graph[low][i] + cost[low] < cost[i])){cost[i] = graph[low][i] + cost[low];parents[i] = low;    //父节点更新必须放到这里,做到时刻更新 }}low = find_lowest_cost(cost, over);over.push_back(low);}int x=5;int y;while (x != 1&& graph[parents[x]][x]>0)   //判断路径是否存在,根据回溯计算长度,这里不用回溯直接用cost中的值也是可以的{y = parents[x];sum = sum + graph[y][x];x = parents[x];}if (x==1) cout << sum << endl;else cout << "Sorry" << endl;}
}

这个代码我是根据这道题目打下来的,主要是为了实现这个算法的代码找的这道题目,不过我没有仔细做这道题,只是利用它实现了Dijkstra算法。

题目如下

5855: 数据结构实验:最短路

时间限制(普通/Java):1000MS/3000MS 内存限制:65536KByte
总提交: 177 测试通过:26

描述

给定n个点的带权有向图,若从顶点x到顶点y之间存在一条路径,那么这条路径的长度定义为路径上各条边的权值之积。

现在请你求出从顶点1到顶点n的最短路径。

输入

第一行为两个正整数n和m(n<=1000,m<=5000),n表示顶点数,m表示边数。

接下来有m行,每行三个正整数x,y,w,表示顶点x到y有一条边权为w的边。

1<=x, y<=n,w不大于10000。

两个顶点之间可能存在多条边。

输出

输出题目定义的最短路径值,由于数可能很大,因此你只需要输出总共有几位数即可。

如果不存在路径,则输出Sorry。

样例输入

3 3
1 2 3
2 3 3
1 3 11

样例输出

1

提示

最短路径为9,1位,因此输出1。

如果觉得有帮助,可以关注一下我的公众号,我的公众号主要是将这些文章进行美化加工,以更加精美的方式展现出来,同时记录我大学四年的生活,谢谢你们!

狄克斯特拉算法(Dijkstra)——算法思想及代码实现相关推荐

  1. 狄克斯特拉(Dijkstra)算法求一个顶点到其余各个顶点的最短路径

    不要自卑,去提升实力 互联网行业谁技术牛谁是爹 如果文章可以带给你能量,那是最好的事!请相信自己 加油o~ 1.狄克斯特拉(Dijkstra)算法 采用狄克斯特拉(Dijkstra)算法可以求带权图( ...

  2. 最短路径--狄克斯特拉(Dijkstra)算法

    最短路径 从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径 Dijkstra算法 算法来源 Dijkstra算法是由一个叫Dijkstra的荷兰人发明的,故称此算法 ...

  3. 狄克斯特拉(Dijkstra)算法原理详细解释与实现(python)

    目录 写在前面 1. 简介 2. 原理 2.1 找出最便宜的节点 2.2 计算前往该节点的各个邻居的开销 2.3 重复上面的步骤 实现 总结 写在前面 本文原理摘自<算法图解>这本书. 其 ...

  4. 算法快学笔记(十三):狄克斯特拉(Dijkstra)算法原理与实现

    1. 简介 广度优先算法可以找出段数最少的路径,但是对于路径上带权重的图,想要找出最快的路径,则需要使用狄克斯特拉算法. 2. 原理 为了说明狄克斯特拉算法的原理,使用换钢琴的的例子来做说明. 假设R ...

  5. 算法图解part7:狄克斯特拉算法

    算法图解part7:狄克斯特拉(Dijkstra)算法 1.狄克斯特拉算法(Dijkstra's algorithm) 2.术语 3.负权边 4.实现狄克斯特拉算法 4.1 最短路径思路 4.2 py ...

  6. Dijkstra(狄克斯特拉)求加权重的邻接矩阵最短路径(初级版)

    算法 参考资源:https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-greedy-algo-7/ 百度百科:迪杰斯特拉算法是 ...

  7. 狄克斯特拉算法(Dijkstra)详细解释

    文章目录 算法用途(目的) 算法思想与本质 图文解释 代码解析 算法用途(目的) 狄克斯特拉算法的用途或者说是目的是计算单源最短路径.单源最短路径的意思是从一个点出发到另外一个点最为的容易.举个例子解 ...

  8. 最短路径--Dijkstra(狄克斯特拉)算法

    最短路径 路径的概念:       在一个无权的图中,若从一顶点到另一顶点存在着一条路径,则称该路径长度为该路径上所经过的边的数目,它等于该路径上的顶点数减 1 .        由于从一顶点到另一顶 ...

  9. 狄克斯特拉(Dijkstra)算法详解

    1.前言 最近在看<算法图解>,其中第七章狄克斯特拉算法个人感觉并没有讲的清楚,比如看完7.1节给人的感觉是狄克斯特拉算法会遍历图中的每一条边,后续狄克斯特拉不适用负权边的说法就站不住脚了 ...

  10. Dijkstra(狄克斯特拉) 算法

    文章目录 加权图 算法步骤 算法实现: 适用场景: 负权边 该算法是一种计算有向无环图最短路径的算法. 加权图 其中每个数字表示时间.要计算非加权图中的最短路径,可使用广度优先搜索.要计算加权图中的最 ...

最新文章

  1. 如何征服Webpack 4并构建一个出色的React应用
  2. 013_JDK的Collections类的sort方法的实现
  3. HTML学习笔记——选择器
  4. jar运行 osgi保存_自动化的OSGi测试运行程序
  5. 交叉报表列头排序时遇到的oracle问题—oracle ORA-12704:字符集不匹配、varchar2转化为nvarchar2字符缺失、case when else后的字符类型要一致...
  6. maven生成jar,提示没有“没有主清单属性
  7. task判断任务是否存在
  8. linux cpu 个数、核心数、线程数
  9. python 断言方法_Python3断言
  10. 基于JSP实现医院病历管理系统,程序员如何在工作中自我增值
  11. 毕设之基金定投小程序
  12. 微信小程序:扫描身份证读取身份信息
  13. 《Python语言程序设计》实验二
  14. 前端如何单枪匹马实现小程序页面级版本控制
  15. JavaScript错误类型
  16. leetcode汇总
  17. 动态壁纸,视频特效- Android
  18. 霍夫变换论文、代码汇总
  19. 安装Oracle Instant Client
  20. android 方块平移动画,Canvas 方块平移动画

热门文章

  1. 百度智能云OCR获取APIKey和SecretKey
  2. and5.1PowerManagerService深入分析(三)updatePowerStateLocked函数
  3. JAVA300集——面向对象编程-类和对象-构造方法及其重载-方法调用
  4. 电脑右下角小喇叭出现红色叉号
  5. vue项目兼容IE浏览器,判断IE11以下,则提示浏览器版本过低,更新浏览器页面
  6. ​CAD图纸怎么转换成PDF格式?这两种方法快速转换
  7. 摸鱼时间,画个吃豆人玩一下
  8. 2020新版软件自动化测试自学全套教程——中级程序员学习路线
  9. JavaScript红皮书学习笔记
  10. decelerate(decelerates)