热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

有向图(4.dijkstra算法详解)

在图的应用中,有一个很重要的需求:我们需要知道从某一个点开始,到其他所有点的最短路径。这其中,Dijkstra算法是典型的最短路径算法。它的关键思想是以起始点为中心,向外一层层扩散,

    在图的应用中,有一个很重要的需求:我们需要知道从某一个点开始,到其他所有点的最短路径。

    这其中,Dijkstra算法是典型的最短路径算法。它的关键思想是以起始点为中心,向外一层层扩散,直到扩展到终点为止。Dijkstra算法能够得出最短路径的最优解,不过它需要遍历计算的节点相当多,所以效率不高。

 

    首先,用最通俗的语言解释。假定有3个顶点,ABC,如图:

 

要求A到其余各点的最短路径。很明显,ACAB更短。有疑惑的是从A->B的最短距离,要么是直接A->B的边,要么是A经过CB的边更短。我们首先找到最短的边(A->C),然后在此基础上扩展,于其余边去对比找到最小值。顶点再进一步扩充增加,按照这个思想,我们总可以找到A到所有点的最短路径。

 

 

 

 

 

算法描述:

 

    从节点1开始到其余各点的dijkstra算法,其中Wa->b表示边a->b的权,d(i)即为最短路径值,顶点集合为V={1,2,3...n}

 

    1.置集合S={1},置顶点集合U={2,3,4...n},数组d(1)=0,d(i)=W1->i1i之间存在边)or 无穷大(1i之间不存在边);

 

 

 

    2.U中,令d(j)=min{d(i),i属于U},将jU中移至S中,若U为空集则算法结束,否则转3

 

 

 

    3.对全部i属于U,如果存在边j->i,那么置d(i)=min{d(i), d(j) + Wj->i},2

 

 

 

    Dijkstra算法的思想为;G=(V, E)是一个带权有向图,把图中顶点集合V分为两部分,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有源点,以后每求出一条最短路径,就将顶点加入到S中,直到所有顶点都加入到S中,算法结束),第二组为其余未求出最短路径的顶点集合(用U表示),按最短路径的长度次序依次将第二组中的顶点加入到第一组中。

 

 

 

     在加入过程中,总保持着从源点vS中各顶点的最短路径不大于从源点vU中各顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离即为源点v到该点的最短路径长度。U中顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短距离。

 

 

 

 

 

算法具体步骤

 

    1.初始时,S中只有源点,即S = {v}v的距离为0(到自己的距离为0)。U包含除v外地所有其他顶点,U中顶点u距离为边上的权(若vu存在边)或 ∞ (vu不存在边,即u不是v的出边邻接点)

 

 

 

    2.U中选取一个距离v最小的顶点k加入到S中(选定的距离就是vk的最短路径)

 

 

 

    3.k为新考虑的中间点,修改U中各顶点的距离。若从源点v经过顶点k到顶点u的距离比原来距离(不经过顶点k)段,则修改顶点u的距离,修改后的距离值为顶点k的距离加上边k->u的权

 

 

 

    4.重复步骤23直到所有的顶点都加入到S

 

 

 

 

 

复杂度分析:

 

    实现方式的不同,可能有n次或者n-1次外层的循环,这里取n次。

 

    步骤2每一轮的比较步骤会是nn-1n-2...1

 

    步骤3每一轮的更新步骤会是n-1n-2...1

 

    这样计算出结果大致为 n²

 

    Dijkstra算法的时间复杂度为O(n^2)

 

    空间复杂度取决于存储方式,邻接矩阵为O(n^2)

 

 

 

再看一个例子:

 


步骤

S集合中      

U集合中

1   

选入A,此时S ={A}

此时最短路径A->A = 0

A为中间点,从A开始找

U = {B, C, D, E, F}

A->B = 6

A->C = 3

A->U中其他顶点 = 

其中A->C = 3 权值为最小,路径最短

2

选入上一轮中找到的最短路径的顶点C,此时S = {A, C}

此时最短路径A->A = 0A->C = 3

C为中间点,从A->C=3这条最短路径开始新一轮查找

U = {B, D, E, F}

A->C->B = 5(比上面的A->B = 6要小)

替换B的权值为更小的A->C->B = 5

A->C->D = 6

A->C->E = 7

A->C->U中其他顶点 = 

其中A->C->B = 5 最短

3

选入B,此时S = {A, C, B}

此时最短路径 A->A = 0A->C = 3

A->C->B = 5

B为中间点,从A->C->B = 5这条最短路径开始新一轮查找

U = {D, E, F}

A->C->B->D = 10(比上面的A->C->D = 6大,不替换,保持D的权值为A->C->D=6)

A->C->B->U中其他顶点 = 

其中 A->C->D = 6 最短

4

选入D,此时 S = {A, C, B, D}

此时最短路径 A->A = 0A->C = 3A->C->B = 5A->C->D = 6

D为中间点,从A->C->D = 6这条最短路径开始新一轮查找

U = {E, F}

A->C->D->E = 8(比上面步骤2中的A->C->E = 7要长,保持E的权值为A->C->E =7)

A->C->D->F = 9

其中A->C->E = 7最短

5

选入E,此时 S = {A, C, B, D ,E}

此时最短路径 A->A = 0A->C = 3A->C->B = 5A->C->D = 6A->C->E =7,

E为中间点,从A->C->E = 7这条最短路径开始新一轮查找

U = {F}

A->C->E->F = 12(比第4步中的A->C->D->F = 9要长,保持F的权值为A->C->D->F = 9)

其中A->C->D->F =9最短

6

选入F,此时 S = {A, C, B, D ,E, F}

此时最短路径 A->A = 0A->C = 3A->C->B = 5A->C->D = 6A->C->E =7,A->C->D->F = 9

U集合已空,查找完毕

 

算法实现:

伪代码

     Dijkstra算法解决了有向图G=(V, E)上带全的单源最短路径问题,但要求所有的边权非负)。因此,假定每条边(uv)E,有w(uv)0

 

     Dijksra算法中设置了一个顶点集合S,从源点s到集合中的顶点的最终最短路径的权值均已确定。算法反复选择具有最短路径估计的顶点uV-S,并将u加入到S中,对u的所有出边进行松弛。在下面的算法实现中,用到了顶点的最小优先队列Q,排序关键字为顶点的d值。

 

DIJSTRA(Gws)

  1 INITIALIZE-SINGLE-SOURCE(Gs)

  2 S ← Φ

  3 Q  V[G]

  4 while Q≠Φ

  5 do u EXTRACT-MIN(Q)

  6 S  S{u}

  7 for each vertex vAdj[u]

  8 do RELAX(uvw)

C++代码实现

    这是前面代码中复制过来的,仍然是用模板跟容器实现的,可以做些修改使用数组或其他数据结构及实现方式。

 

template
int OLGraph::Dijkstra(IN const vertexNameType vertexName1)
{
int sourceIndex = getVertexIndex(vertexName1); //获取源点在容器中索引值
if (-1 == sourceIndex)
{
cerr <<"There is no vertex " <return false;
}
int nVertexNo = getVertexNumber(); //获取顶点数
vector<bool> vecIncludeArray; //顶点是否已求出最短路径
vecIncludeArray.assign(nVertexNo, false); //初始化容器
vecIncludeArray[sourceIndex] = true;
vector vecDistanceArray; //路径值容器
vecDistanceArray.assign(nVertexNo, weight(INT_MAX)); //将所有顶点到源点的初始路径值为正无穷
vecDistanceArray[sourceIndex] = weight(0); //源点到自己距离置0
vector<int> vecPrevVertex; //路径中,入边弧尾顶点编号(即指向自己那个顶点的编号)
vecPrevVertex.assign(nVertexNo, sourceIndex); //指向所有顶点的弧尾都初始为源点,源点指向所有顶点
getVertexEdgeWeight(sourceIndex, vecDistanceArray); //得到源点到其余每个顶点的距离
int vFrom, vTo;
while(1)
{
weight minWeight = weight(INT_MAX);
vFrom = sourceIndex;
vTo = -1;
for (int i = 0; i //找出还没求出最短距离的顶点中,距离最小的一个
{
if (!vecIncludeArray[i] && minWeight > vecDistanceArray[i])
{
minWeight = vecDistanceArray[i];
vFrom = i;
}
}
if (weight(INT_MAX) == minWeight) //若所有顶点都已求出最短路径,跳出循环
{
break;
}
vecIncludeArray[vFrom] = true; //将找出的顶点加入到已求出最短路径的顶点集合中
//更新当前最短路径,只需要更新vFrom顶点的邻接表即可,因为所有vFrom指向的边都在邻接表中
Edge *p = m_vertexArray[vFrom].firstout;
while (NULL != p)
{
weight wFT = p->edgeWeight;
vTo = p->headvex;
if (!vecIncludeArray[vTo] && vecDistanceArray[vTo] > wFT + vecDistanceArray[vFrom]) //当前顶点还未求出最短路径,并且经由新中间点得路径更短
{
vecDistanceArray[vTo] = wFT + vecDistanceArray[vFrom];
vecPrevVertex[vTo] = vFrom;
}
p = p->tlink;
}

}
for (int i = 0; i //输出最短路径
{
if (weight(INT_MAX) != vecDistanceArray[i])
{
cout <"->" <": ";
DijkstraPrint(i, sourceIndex, vecPrevVertex);
cout <<" " <cout <}
}
return 0;
}
template
void OLGraph::DijkstraPrint(IN int index, IN int sourceIndex, IN vector<int> vecPreVertex)
{
if (sourceIndex != index)
{
DijkstraPrint(vecPreVertex[index], sourceIndex, vecPreVertex);
}
cout <" ";
}


 

 


推荐阅读
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
author-avatar
小岳不在家
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有