图论中一个经典问题就是求最短路。最为基础和最为经典的算法莫过于 Dijkstra 和 Floyd 算法,一个是贪心算法,一个是动态规划。这也是算法中的两大经典代表。用一个简单图在纸上一步一步演算,也是非常好理解的。理解透自己多默写几次就可以记住,机试时基本的工作往往就是高速构造邻接矩阵了。

对于平时的练习,一个非常厉害的 ACMer  @BenLin_BLY 说:“刷水题能够加快我们编程的速度,做经典则能够让我们触类旁通,初期假设遇见非常多编不出。最好还是就写伪代码,理思路。在纸上进行总体分析和一步步的演算,然后在转换成代码。再重复迭代”。Linus 不是也说了 "Nobody actually creates perfect code the first time
around, except me. But there’s only one of me."  ^_^  对于算法的学习,绝大多数都是把问题分解变形,然后套用曾经或别人的思路。

仅仅有把经典的算法都烂熟于心时,解决这个问题时才干够做到不知不觉的套用已有的思路和经验。能让我们走的更远的仅仅有热情和方法!

当认准一件事有价值时,我们要做的就是长期坚持,既然熟练掌握经典算法这么有价值。那么接下来要做的就是重复训练直到烂熟于心 ^_^

单源最短路

给定起点 start, 求到随意点的最短路 Dijkstra 算法,前提不能有负权边和孤立点:

  • 贪心算法:每次找近期的点,局部最优等于全局最优,数学归纳法可证
  • 维护起点 start 到每一个点的距离
  • 时间复杂度 O(n^2)
  • 附加空间复杂度 O(n)
  1. Dijkstra 算法伪代码:
  2. Q = {} // 已求出到 start 点最短路的点集合,初始为空
  3. d[s] = 0, 其余值为正无穷大
  4. while (|Q| < |V|) // 数学符号|A|表示集合A的点数
  5. 取出不在Q中的最小的d[i]
  6. for (i相邻的点jj不属于Q)
  7. d[j] = min(d[j], d[i] + c[i][j]) //维护距离
  8. Q = Q + {i}

全局最短路

求出图中随意两点最短路,利用 Floyd 算法,对负权边仍然有效:

  • 动态规划:每次增加一个点
  • 维护随意两点间的距离
  • 时间复杂度 O(n^3)
  • 附加空间复杂度 O(n^2)
  1. Floyd 算法伪代码: # 直接改成 Python 了。没办法就是喜欢 Python :)
  2. for k in range(0, n):
  3. for i in range(0, n):
  4. for j in range(0, n):
  5. g[i][j] = min(g[i][j], g[i][k]+g[k][j])

小实验

一个图例如以下所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGhpc2lubm9jZW5jZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

源码 ( C 实现)

  1. #include <stdio.h>
  2. #define N 65536
  3.  
  4. /* 计算有 9 个点的图的单源最短路 */
  5. void Dijkstra(int graph[9][9], int start, int path[9]){
  6. int num=9, min, vertex, i, j;
  7. int flag[num];
  8. for(i = 0; i < num; ++i){ //初始化全部点都未计算,第一次距离直接读邻接矩阵
  9. path[i] = graph[start][i];
  10. flag[i] = 0;
  11. }
  12.  
  13. for(i = 0; i < num; ++i){
  14. min = N;
  15. for(j = 0; j < num; ++j){
  16. if(flag[j] == 0 && min > path[j]){ //求未计算过的点中距离 start 近期点
  17. min = path[j];
  18. vertex = j;
  19. }
  20. }
  21. flag[vertex] = 1; //将上面计算的点标记为计算过
  22. for(j = 0; j < num; ++j){ //维护全部未计算过点的距离
  23. if(flag[j] == 0 && path[j] > path[vertex] + graph[vertex][j]){
  24. path[j] = path[vertex] + graph[vertex][j];
  25. }
  26. }
  27. }
  28. }
  29.  
  30. /* 计算一个 9 个点的图全部点到全部点间的最短路 */
  31. void Floyd(int graph[9][9]){
  32. int num = 9, i, j, k;
  33. for(k = 0; k < num; ++k){ //中转点
  34. for(i = 0; i < num; ++i){ //出发点
  35. for(j = 0; j < num; ++j){ //到达点
  36. if(graph[i][j] > graph[i][k] + graph[k][j]){
  37. graph[i][j] = graph[i][k] + graph[k][j];
  38. }
  39. }
  40. }
  41. }
  42. }
  43.  
  44. int main() {
  45. int graph[9][9]={
  46. {0, 1, 5, N, N, N, N, N, N},
  47. {1, 0, 3, 7, 5, N, N, N, N},
  48. {5, 3, 0, N, 1, 7, N, N, N},
  49. {N, 7, N, 0, 2, N, 3, N, N},
  50. {N, 5, 1, 2, 0, 3, 6, 9, N},
  51. {N, N, 7, N, 3, 0, N, 5, N},
  52. {N, N, N, 3, 6, N, 0, 2, 7},
  53. {N, N, N, N, 9, 5, 2, 0, 4},
  54. {N, N, N, N, N, N, 7, 4, 0}
  55. };
  56. int start=0, path[9], i, j;
  57. Dijkstra(graph, start, path); //计算结果写入 path 数组
  58. printf("点 %d 到全部点的最短距离:\n", start);
  59. for(i=0; i<9; ++i){
  60. printf("%d ",path[i]);
  61. }
  62.  
  63. Floyd(graph); //计算后的结果也直接写入graph
  64. printf("\n全部点到全部点的最短距离\n");
  65. for(i=0; i<9; ++i){
  66. for(j=0; j<9; ++j){
  67. printf("%d ",graph[i][j]);
  68. }
  69. printf("\n");
  70. }
  71.  
  72. return 0;
  73. }
  74.  
  75. /****** 执行结果 ***********
  76. 点 0 到全部点的最短距离:
  77. 0 1 4 7 5 8 10 12 16
  78. 全部点到全部点的最短距离
  79. 0 1 4 7 5 8 10 12 16
  80. 1 0 3 6 4 7 9 11 15
  81. 4 3 0 3 1 4 6 8 12
  82. 7 6 3 0 2 5 3 5 9
  83. 5 4 1 2 0 3 5 7 11
  84. 8 7 4 5 3 0 7 5 9
  85. 10 9 6 3 5 7 0 2 6
  86. 12 11 8 5 7 5 2 0 4
  87. 16 15 12 9 11 9 6 4 0
  88. *****************************/

算法学习笔记(三) 最短路 Dijkstra 和 Floyd 算法的更多相关文章

  1. Johnson算法学习笔记

    \(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...

  2. Johnson 全源最短路径算法学习笔记

    Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...

  3. 学习笔记(三)--->《Java 8编程官方参考教程(第9版).pdf》:第十章到十二章学习笔记

    回到顶部 注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法 ...

  4. 某科学的PID算法学习笔记

    最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...

  5. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  6. [Firefly引擎][学习笔记三][已完结]所需模块封装

    原地址:http://www.9miao.com/question-15-54671.html 学习笔记一传送门学习笔记二传送门 学习笔记三导读:        笔记三主要就是各个模块的封装了,这里贴 ...

  7. JSP学习笔记(三):简单的Tomcat Web服务器

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  8. C / C++算法学习笔记(8)-SHELL排序

    原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...

  9. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

随机推荐

  1. Java学习之DAO设计模式

    DAO设计模式是一个javaEE里的设计模式,DAO是Data Access Object 数据访问接口. 一个典型的DAO实现有三个组件: 1.一个DAO接口 2.一个DAO接口的具体类: 3.数据 ...

  2. 微软正式公布Visual Studio 2013 Update 3 (2013.3) RTM

     昨天微软的Visual Studio 2013 Update 3(Visual Studio 2013.3)正式公布(RTM)了,做为微软认证金牌合作的葡萄城控件,我们组织力量第一时间进行翻译. ...

  3. Oracle PL/SQL 游标

    在PL/SQL块中执行SELECT.INSERT.DELETE和UPDATE语句时,ORACLE会在内存中为其分配上下文区(Context Area),即缓冲区.游标是指向该区的一个指针,或是命名一个 ...

  4. Request url 各种属性值

    網址:http://localhost:1897/News/Press/Content.aspx/123?id=1#toc Request.ApplicationPath / Request.Phys ...

  5. Jquery Select 下拉框处理

    $("#select").empty();//清空 $("#select").append($("<option/>").val ...

  6. python之filter过滤器

    Python内建的filter()函数用于过滤序列. 和map()类似,filter()也接收一个函数和一个序列.和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是 ...

  7. 再谈协方差矩阵之主成分分析PCA

    上次那篇文章在理论层次介绍了下协方差矩阵,没准很多人觉得这东西用处不大,其实协方差矩阵在好多学科里都有很重要的作用,比如多维的正态分布,再比如今天我们今天的主角——主成分分析(Principal Co ...

  8. Android的回调模拟

    想要彻底理解安卓中用的回调,最好的办法是自己写一个类似的实现安卓中回调功能的实现方法. 我自己写了一个可以实现setOnClickListener回调的工程: 具体目录: 工程源码的具体地址:http ...

  9. Non recursive Depth first search

    深度优先非递归实现算法: 1 递归算法: //初始化相关数据结构 DFS(G) ------------------------------------------------------------ ...

  10. SQL Identity自增列清零方法

    1.使用DBCC控制台命令: dbcc checkident(表名,RESEED,0) 2.truncate table 也可将当前标识值清零 但当有外键等约束时,无法truncate表 可以先禁用外 ...