单源最短路径(1):Dijkstra 算法

时间:2022-05-12 12:33:48

一:背景

Dijkstra 算法(中文名:迪杰斯特拉算法)是由荷兰计算机科学家 Edsger Wybe Dijkstra 提出。该算法常用于路由算法或者作为其他图算法的一个子模块。举例来说,如果图中的顶点表示城市,而边上的权重表示城市间开车行经的距离,该算法可以用来找到两个城市之间的最短路径。

二:算法过程

我们用一个例子来具体说明迪杰斯特拉算法的流程。

单源最短路径(1):Dijkstra 算法

定义源点为 0,dist[i]为源点 0 到顶点 i 的最短路径。其过程描述如下:

单源最短路径(1):Dijkstra 算法

第 1 步:从源点 0 开始,找到与其邻接的点:1,2,3,更新dist[]数组,因 0 不与 4 邻接,故dist[4]为正无穷。在dist[]中找到最小值,其顶点为 2,即此时已找到 0 到 2 的最短路。

第 2 步:从 2 开始,继续更新dist[]数组:2 与 1 不邻接,不更新;2 与 3 邻接,因0→2→3比dist[3]大,故不更新dist[3] ;2 与 4 邻接,因0→2→4比dist[4]小,故更新dist[4]为 4。在dist[]中找到最小值,其顶点为 3,即此时又找到 0 到 3 的最短路。

第 3 步:从 3 开始,继续更新dist[]数组:3 与 1 邻接,因0→3→1比dist[1]小,更新dist[1]为 5;3 与 4 邻接,因0→3→4比dist[4]大,故不更新。在dist[]中找到最小值,其顶点为 4,即此时又找到 0 到 4 的最短路。

第 4 步:从 4 开始,继续更新dist[]数组:4 与 1 不邻接,不更新。在dist[]中找到最小值,其顶点为 1,即此时又找到 0 到 1 的最短路。

第 5 步:所有点都已找到,停止。

对于上述步骤,你可能存在以下的疑问:

单源最短路径(1):Dijkstra 算法

若 A 作为源点,与其邻接的只有 B,C,D 三点,其dist[]最小时顶点为 C,即就可以确定A→C为 A 到 C 的最短路。但是我们存在疑问的是:是否还存在另一条路径使 A 到 C 的距离更小? 用反证法证明。

假设存在如上图的红色虚线路径,使A→D→C的距离更小,那么A→D作为A→D→C的子路径,其距离也比A→C小,这与前面所述 “dist[]最小时顶点为 C” 矛盾,故假设不成立。因此这个疑问不存在。

根据上面的证明,我们可以推断出,Dijkstra 每次循环都可以确定一个顶点的最短路径,故程序需要循环 n-1 次

三:完整代码

 #include<iostream>
 using namespace std;

 ][];  //邻接矩阵
 ];     //标记数组
 ];         //源点到顶点i的最短距离
 ];         //记录最短路的路径
 int source;            //源点
 int vertex_num;        //顶点数
 int arc_num;           //弧数

 void Dijkstra(int source)
 {
     memset(visited, , sizeof(visited));  //初始化标记数组
     visited[source] = true;
     ; i < vertex_num; i++)
     {
         dist[i] = matrix[source][i];
         path[i] = source;
     }

     int min_cost;        //权值最小
     int min_cost_index;  //权值最小的下标
     ; i < vertex_num; i++)  //找到源点到另外vertex_num-1个点的最短路径
     {
         min_cost = INT_MAX;
         ; j < vertex_num; j++)
         {
             if (visited[j] == false && dist[j] < min_cost)  //找到权值最小
             {
                 min_cost = dist[j]; min_cost_index = j;
             }
         }

         visited[min_cost_index] = true;  //该点已找到,进行标记

         ; j < vertex_num; j++)  //更新dist数组
         {
             if (visited[j] == false &&
                 matrix[min_cost_index][j] != INT_MAX &&  //确保两点之间有弧
                 matrix[min_cost_index][j] + min_cost < dist[j])
             {
                 dist[j] = matrix[min_cost_index][j] + min_cost;
                 path[j] = min_cost_index;
             }
         }
     }
 }

 int main()
 {
     cout << "请输入图的顶点数(<100):";
     cin >> vertex_num; cout << "请输入图的弧数:";
     cin >> arc_num;

     ; i < vertex_num; i++)
         ; j < vertex_num; j++)
             matrix[i][j] = INT_MAX;  //初始化matrix数组

     cout << "请输入弧的信息:\n";
     int u, v, w;
     ; i < arc_num; i++)
     {
         cin >> u >> v >> w;
         matrix[u][v] = matrix[v][u] = w;
     }

     cout << "请输入源点(<" << vertex_num << "):";
     cin >> source;
     Dijkstra(source);

     ; i < vertex_num; i++)
     {
         if (i != source)
         {
             cout << source << "到" << i << "最短距离是:" << dist[i] << ",路径是:" << i;
             int t = path[i];
             while (t != source)
             {
                 cout << "--" << t; t = path[t];
             } cout << "--" << source << endl;
         }
     } 

     ;
 }

输入数据,结果为:

单源最短路径(1):Dijkstra 算法

四:时间复杂度

设图的边数为 m,顶点数为 n。

Dijkstra 算法最简单的实现方法是用一个数组来存储所有顶点的dist[](即本程序采用的策略),所以搜索dist[]中最小元素的运算需要线性搜索O(n)。这样的话算法的运行时间是O(n^2) 。
 对于边数远少于n^2 的稀疏图来说,我们可以用邻接表来更有效的实现该算法。同时需要将一个二叉堆或者斐波纳契堆用作优先队列来查找最小的顶点。当用到二叉堆的时候,算法所需的时间为O((m+n)logn),斐波纳契堆能稍微提高一些性能,让算法运行时间达到O(m+nlogn)。

然而,使用斐波纳契堆进行编程,常常会由于算法常数过大而导致速度没有显著提高。

关于O((m+n)logn)的由来,我简单的证明了下(仅个人看法,不保证其正确性):

1.把dist[]数组调整成最小堆,需要O(n) 的时间;

2.因为是最小堆,所以每次取出最小值只需 O(1)的时间,接着把数组尾的数放置堆顶,并花费O(logn) 的时间重新调整成最小堆;

3.我们需要 n-1 次操作才可以找出剩下的 n-1 个点,在这期间,大约需要访问 m 次边,每次访问都可能造成dist[]的改变,因此还需要 O(logn)的时间来进行最小堆的重新调整(从当前dist[]改变的位置往上调整)。

4.综上所述:总的时间复杂度为:O(n)+O(nlogn)+O(mlogn)=O((m+n)logn)

5.优先队列,把每个顶点的序号和其dist[]压在一个结构体再放进队列里;

6.  自己建一个小顶堆heap[],存储顶点序号,再用一个数组pos[]记录第 i 个顶点在堆中的位置。相比之下,前者的编码难度较低,因此在平时编程甚至算法竞赛中,都是首选。

五:该算法的缺陷

Dijkstra 算法有个巨大的缺陷,请考虑下面这幅图:

单源最短路径(1):Dijkstra 算法

u→v间存在一条负权回路(所谓的负权回路,维基和百科并未收录其名词,但从网上的一致态度来看,其含义为:如果存在一个环(从某个点出发又回到自己的路径),而且这个环上所有权值之和是负数,那这就是一个负权环,也叫负权回路),那么只要无限次地走这条负权回路,便可以无限制地减少它的最短路径权值,这就变相地说明最短路径不存在。一个不存在最短路径的图,Dijkstra 算法无法检测出这个问题,其最后求解的dist[]也是错的。

那么对于上述的 “一个不存在最短路径的图”,我们该用什么方法来解决呢?请接着看本系列第二篇文章。

以上转载自:http://www.61mon.com/index.php/archives/194/

单源最短路径(1):Dijkstra 算法的更多相关文章

  1. 单源最短路径(dijkstra算法)php实现

    做一个医学项目,当中在病例评分时会用到单源最短路径的算法.单源最短路径的dijkstra算法的思路例如以下: 如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点.那么( ...

  2. 【算法导论】单源最短路径之Dijkstra算法

    Dijkstra算法解决了有向图上带正权值的单源最短路径问题,其运行时间要比Bellman-Ford算法低,但适用范围比Bellman-Ford算法窄. 迪杰斯特拉提出的按路径长度递增次序来产生源点到 ...

  3. 单源最短路径:Dijkstra算法(堆优化)

    前言:趁着对Dijkstra还有点印象,赶快写一篇笔记. 注意:本文章面向已有Dijkstra算法基础的童鞋. 简介 单源最短路径,在我的理解里就是求从一个源点(起点)到其它点的最短路径的长度. 当然 ...

  4. 单源最短路径问题-Dijkstra算法

    同样是层序遍历,在每次迭代中挑出最小的设置为已知 ===================================== 2017年9月18日10:00:03 dijkstra并不是完全的层序遍历 ...

  5. 单源最短路径问题&lpar;dijkstra算法 及其 优化算法&lpar;优先队列实现&rpar;&rpar;

    #define _CRT_SECURE_NO_WARNINGS /* 7 10 0 1 5 0 2 2 1 2 4 1 3 2 2 3 6 2 4 10 3 5 1 4 5 3 4 6 5 5 6 9 ...

  6. 【算法设计与分析基础】25、单起点最短路径的dijkstra算法

    首先看看这换个数据图 邻接矩阵 dijkstra算法的寻找最短路径的核心就是对于这个节点的数据结构的设计 1.节点中保存有已经加入最短路径的集合中到当前节点的最短路径的节点 2.从起点经过或者不经过 ...

  7. 【算法导论】单源最短路径之Bellman-Ford算法

    单源最短路径指的是从一个顶点到其它顶点的具有最小权值的路径.我们之前提到的广度优先搜索算法就是一种无权图上执行的最短路径算法,即在所有的边都具有单位权值的图的一种算法.单源最短路径算法可以解决图中任意 ...

  8. 单源最短路——dijkstra算法

    Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 问 ...

  9. 单源点最短路径的Dijkstra算法

    在带权图(网)里,点A到点B所有路径中边的权值之和为最短的那一条路径,称为A,B两点之间的最短路径;并称路径上的第一个顶点为源点(Source),最后一个顶点为终点(Destination).在无权图 ...

  10. 单源最短路Dijkstra算法——matlab实现

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止. 基本思想 通过Dijk ...

随机推荐

  1. HTML5之API

    HTML5就是牛,可以直接播放音视频,还可以作图: 一.HTML5中播放视频和音频: 加载时直接播放音频的方式:new Audio("BY2.mp3").play(); <d ...

  2. 数据分析&NotEqual;Hadoop&plus;NoSQL,不妨先看完善现有技术的10条捷径&lpar;分享&rpar;

            Hadoop让大数据分析走向了大众化,然而它的部署仍需耗费大量的人力和物力.在直奔Hadoop之前,是否已经将现有技术推向极限?这里总结了对Hadoop投资前可以尝试的10个替代方案, ...

  3. java制作证书的工具keytool用法

    一.keytool的概念 keytool 是个密钥和证书管理工具.它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据完整性以及认证服务.在 ...

  4. 关于&commat;see注解

    所有三种类型的注释文档都可包含@see标记,它允许我们引用其他类里的文档.对于这个标记,javadoc会生成相应的HTML,将其直接链接到其他文档.格式如下: @see 类名@see 完整类名@see ...

  5. C&num; static 干货全解析

    讲解顺序 背景 静态字段 静态函数 静态方法 疑问解答 背景 static来源 在编写类的时候,有时候需要类里面的某个成员具有唯一性,也就是,对所有的对象都保持只有一个的状态.比如创建个人信息,我们都 ...

  6. Canvas裁剪和Region、RegionIterator

    主要是看这边文章学习:http://blog.csdn.net/lonelyroamer/article/details/8349601 Region.op参数 DIFFERENCE(0), //最终 ...

  7. Myeclipse10、Maven构建Javaweb项目

    主要介绍如何使用 Myeclipse 10 构建 Maven Web 项目,关于 Maven 的介绍就略过了. 工具/原料   myeclipse apache-maven-3.1.0 方法/步骤   ...

  8. Network of Schools POJ - 1236&lpar;强连通&plus;缩点&rpar;

    题目大意 有N个学校,这些学校之间用一些单向边连接,若学校A连接到学校B(B不一定连接到A),那么给学校A发一套软件,则学校B也可以获得.现给出学校之间的连接关系,求出至少给几个学校分发软件,才能使得 ...

  9. RSA modulus too small&colon; 512 &lt&semi; minimum 768 bits

    RSA modulus too small: 512 < minimum 768 bits $ ssh admin@192.168.50.46 ssh_rsa_verify: RSA modul ...

  10. Java工程师成神之路 转

      一.基础篇 1.1 JVM 1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http://www.jcp.org/en/jsr/detail?id=133 http:/ ...