浅谈数据结构-最短路径
最短路径和最小生成树在应用很是不同的,比如:一开始修建一条地铁,然后在地铁点上有多个点,需要修建一个路程最短的地铁线,将这些地铁点连接起来,这就是最小生成树(点与点之间距离是已知的)。小强需要从A点去B点旅游,中间会经过好几个点,需要找出条最短路径到达B点。从应用上明显看出,两者的目的不同、初始化条件也是不同的。
一、Dijkstra(迪杰斯特拉)算法
Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
1、算法思想
令G = (V,E)为一个带权有向图,把图中的顶点集合V分成两组,第一组为已求出最短路径的顶点集合S(初始时S中只有源节点,以后每求得一条最短路径,就将它对应的顶点加入到集合S中,直到全部顶点都加入到S中);第二组是未确定最短路径的顶点集合U。在加入过程中,总保持从源节点v到S中各顶点的最短路径长度不大于从源节点v到U中任何顶点的最短路径长度。
针对上图建立的两个集合,之后运用Dijkstra算法运行后,两个集合中元素为将ABCDEF在数组中坐标为1,2,3,4,5,6:
迭代 | S | U | dist[2] | dist[3] | dist[4] | dist[5] | dist[6] |
1 | A | - | 6 | 3 | MAX | MAX | MAX |
2 | A,C | C | 5 | 3 | 6 | 7 | MAX |
3 | A,C,B | B | 5 | 3 | 6 | 7 | MAX |
4 | A,C,B,D | D | 5 | 3 | 6 | 7 | 9 |
5 | A,C,B,D,E | E | 5 | 3 | 6 | 7 | 9 |
6 | A,B,C,D,E,F | F | 5 | 3 | 6 | 7 | 9 |
2、算法分析
根据上面分析,得知需要创建两个数组,创建顶点集合,还有边集合,用于保存点到各个边的权重,然后在权重集合选取最小的权值边对的顶点,然后继续循环。
- 创建顶点结合nNodeIndex,初始化为0,数组中为1是,表示对应的顶点已经添加到最短路径顶点集合S了。
- 创建初始顶点到各个顶点的边集合,保存此顶点到各个顶点的距离(权重),用邻接矩阵中行元素初始化(类似最小生成树)。
- 循环计算此顶点到各个顶点的最小值,得知后nNodeIndex[i] = 1,同时更新边集合 的数值。
- 总体上讲还是比较容易的。
3、例图解释
4、代码
//Dijkstra算法 void GraphData::ShortPath_Dijkstra(GraphArray *pArray) {//Dijkstra算法和最小生成树的算法,在某种程度是相似的int min,i,j,k;int nNodeIndex[MAXVEX]; //保存相关顶点坐标,1就是已经遍历访问的过结点(在最小生成树中为数值表示遍历过同时值与坐标是一条边)int nNodeWeight[MAXVEX]; //保存某个顶点到各个顶点的权值,为不为0和最大值表示遍历过了。int nPathLength[MAXVEX]; //坐标和元素表示为同时值和坐标表示一边,与Primes有相同的//两个数组的初始化printf("开始初始化,当前顶点边的权值为:");for(i = 0;i<pArray->numVertexes;i++){nNodeIndex[i] = 0; nNodeWeight[i] = pArray->arg[0][i];//设定在矩阵中第一个顶点为初始点。nPathLength[i] = 0;printf(" %c",nNodeWeight[i]);}nNodeWeight[0] = 0; //选取坐标点0为起始点。nNodeIndex[0] = 1; //这样0就是起始点,设为1.(和Prime的不同)//算法思想for (i = 1;i< pArray->numVertexes;i++){min = INFINITY; //初始化权值为最大值;j = 1;k = 0;// 循环全部顶点,寻找与初始点边权值最小的顶点,记下权值和坐标while(j < pArray->numVertexes){//如果权值不为0,且权值小于min,为0表示本身if (!nNodeIndex[j]&&nNodeWeight[j] < min) //这里Prime是权重中不为0, {min = nNodeWeight[j];k = j; //保存上述顶点的坐标值 }j++;}printf("当前顶点边中权值最小边(%d,%d)\n",nNodeIndex[k] , k); //打印当前顶点边中权值最小//nNodeWeight[k] = 0; //将当前顶点的权值设置为0,表示此顶点已经完成任务nNodeIndex[k] = 1; //将目前找到的最近的顶点置为1//for (j = 1;j< pArray->numVertexes;j++) //循环所有顶点,查找与k顶点的最小边//{// //若下标为k的顶点各边权值小于此前这些顶点未被加入的生成树权值// if (nNodeWeight[j] != 0&&pArray->arg[k][j] < nNodeWeight[j])// {// nNodeWeight[j] = pArray->arg[k][j];// nNodeIndex[j] = k; //将下标为k的顶点存入adjvex// }//}//修正当前最短路径及距离for (j = 1;j< pArray->numVertexes;j++) //循环所有顶点,查找与k顶点的最小边 {//若下标为k的顶点各边权值小于此前这些顶点未被加入的生成树权值if (!nNodeIndex[j] && pArray->arg[k][j] + min< nNodeWeight[j]){nNodeWeight[j] = pArray->arg[k][j] + min;nPathLength[j] = k; //将下标为k的顶点存入adjvex }}//打印当前顶点状况printf("坐标点数组为:");for(j = 0;j< pArray->numVertexes;j++){printf("%3d ",nPathLength[j]);}printf("\n");printf("权重数组为:");for(j = 0;j< pArray->numVertexes;j++){printf("%3d ",nNodeWeight[j]);}printf("\n");}}
5、代码分析
上图就是图的邻接矩阵,对应上面的例图分析,我们分析下,代码运算结果。
上图是代码运算后的结果,首先是第一次,最小边为(0,2),加入的顶点是C。第二次最小边是(0,1),加入的结果是B。等等,会发现结果同前面例图分析结果一样。
最后的权重数组为(0,5,3,6,7,9)意味着顶点A到B,C,D,E,F的距离是5,3,6,7,9。A->B:5,根据坐标点数组nPathLength[1] = 2,意味着经过坐标为2的顶点C,再看nPathLength[2] = 0,结束。再比如A->D:6,nPathLength[4] = 2,所以为A->C->D.同理其他路劲也是如此寻找。
二、Floyd算法
Dijkstra算法解决了某个源点到其余各个顶点的最短距离问题。从循环语句上判断,算法的时间复杂度是O(n2)。在循环的外面再加一个循环,也就成了所有顶点的最短距离。此时算法的复杂度就是O(n3).
弗洛依德(Floyd)算法就是一个事件复杂度为O(n)的算法,只不过算法非常简洁优雅。
1、算法思想
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)。
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
2、算法分析
- 创建一个矩阵,记录两个顶点的权值。在DIjkstra算法中是记录一个顶点到其他顶点的路径长度,声明一个数组,此处是各个顶点,所以为矩阵。
- 创建一个矩阵,记录顶点到另个顶点路径的走法,这个后面会讲解。
- Floyd算法思想:对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。将权值鞠振宁更新,同样还有路径矩阵。
3、例图解释
(1)程序开始运行,第4-11行就是初始化了D和P,使得它们成为 上图 的两个矩阵。从矩阵也得到,v0->v1路径权值为1,v0->v2路径权值为5,v0->v3无边连线,所以路径权值为极大值65535。
(2)第12~25行,是算法的主循环,一共三层嵌套,k代表的就是中转顶点的下标。v代表起始顶点,w代表结束顶点。
(3)当k = 0时,也就是所有的顶点都经过v0中转,计算是否有最短路径的变化。可惜结果是,没有任何变化,如下图所示。
(4)当k = 1时,也就是所有的顶点都经过v1中转。此时,当v = 0 时,原本D[0][2] = 5,现在由于D[0][1] + D[1][2] = 4。因此由代码的的第20行,二者取其最小值,得到D[0][2] = 4,同理可得D[0][3] = 8、D[0][4] = 6,当v = 2、3、4时,也修改了一些数据,请看下图左图中虚线框数据。由于这些最小权值的修正,所以在路径矩阵P上,也要做处理,将它们都改为当前的P[v][k]值,见代码第21行。
(5)接下来就是k = 2,一直到8结束,表示针对每个顶点做中转得到的计算结果,当然,我们也要清楚,D0是以D-1为基础,D1是以D0为基础,......,D8是以D7为基础的。最终,当k = 8时,两个矩阵数据如下图所示。
至此,我们的最短路径就算是完成了。可以看到矩阵第v0行的数值与迪杰斯特拉算法求得的D数组的数值是完全相同。而且这里是所有顶点到所有顶点的最短路径权值和都可以计算出。
那么如何由P这个路径数组得出具体的最短路径呢?以v0到v8为例,从上图的右图第v8列,P[0][8]= 1,得到要经过顶点v1,然后将1取代0,得到P[1][8] = 2,说明要经过v2,然后2取代1得到P[2][8] = 4,说明要经过v4,然后4取代2,得到P[4][8]= 3,说明要经过3,........,这样很容易就推倒出最终的最短路径值为v0->v1->v2->v4->v3->v6->v7->v8。
4、示例代码
//Floyd算法 void GraphData::ShortPath_Floyd(GraphArray *pArray) {int i,j,m,k;int nNodeIndex[MAXVEX][MAXVEX];int nNodeWeight[MAXVEX][MAXVEX];for ( i = 0;i< pArray->numVertexes;i++){for (j = 0;j< pArray->numVertexes;j++){ nNodeIndex[i][j] = j; /* 初始化 */nNodeWeight[i][j] = pArray->arg[i][j]; /* [i][j]值即为对应点间的权值 */}}for (i = 0;i< pArray->numVertexes;i++){for (j = 0;j< pArray->numVertexes;j++){for (k = 0;k<pArray->numVertexes;k++){if (pArray->arg[j][k] > pArray->arg[j][i] + pArray->arg[i][k]){/* 如果经过下标为k顶点路径比原两点间路径更短 */nNodeWeight[j][k] = pArray->arg[j][i] + pArray->arg[i][k]; /* 将当前两点间权值设为更小的一个 */nNodeIndex[j][k] = nNodeIndex[j][i]; /* 路径设置为经过下标为k的顶点 */}}}}for (i = 0; i< pArray->numVertexes;i++){for (j = 0;j< pArray->numVertexes;j++){printf("v%d-v%d weight: %d",i,j,nNodeWeight[i][j]);m = nNodeIndex[i][j]; //获得第一个路径点的顶点下标printf("path :%d",i); //打印源点while(m!=j){printf(" -> %d",m); //打印路径顶点m = nNodeIndex[m][j]; //获取下一个路径顶点下标 }printf(" -> %d\n",m); //打印路径终点。 }printf("\n");} }
5、代码分析
同样对于Dijkstra算法中,同样的邻接矩阵,我们最后发现其中v0到各个顶点数据与Dijkstra中数据一样奥,同时显示出路径中通过的顶点。
转载于:https://www.cnblogs.com/polly333/p/4767051.html
浅谈数据结构-最短路径相关推荐
- 浅谈数据结构和数据类型
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u012540337/article/details/80499226 最近总是被这两个概念混淆,抽出 ...
- 【浅谈数据结构】《数据结构》Data Structure
<数据结构>60' 一.栈(stack).队列(Queue).向量(Vector) 1.链表 带哨兵节点链表了解清楚 链表要会写,会分析.各种链表. 2.栈 LIFO(last in fi ...
- 浅谈:数据结构之双链表结构与代码模拟双链表的实现
双链表 本文是观看尚硅谷韩老师数据结构与算法根据老师讲解自己做的笔记,部分信息收集网络 与单链表区别 逻辑上没有区别.他们均是完成线性表的内容.主要的区别是结构上的构造有所区别. 对于单链表: 对于一 ...
- 浅谈数据结构之主席树(线段树进阶版)
今天看了点主席树的概念,加上飞哥上次讲的,目前对主席树有了大致的了解,简单谈谈吧,不讲代码,只讲思路,日后贴题! Orz高级数据结构发明者主席!!最早在CLJ的课件里第一次看到了这个词,最近做区间第K ...
- java 算法_Java 浅谈数据结构和算法
以前不管自己还是朋友在面试java工程师岗位的时候,都会被问到这样的问题: "介绍下java中的数据结构和算法". 很多朋友被问到的时候发现无从下口,甚至特别是一些初级java工程 ...
- 浅谈数据结构以及其特点
文档声明: 以下资料均属于本人在学习过程中产出的学习笔记,如果错误或者遗漏之处,请多多指正.并且该文档在后期会随着学习的深入不断补充完善. 资料仅供学习交流使用. 作者:Aliven888 1.简介: ...
- 浅谈数据结构-关键路径
上一章节讲解了拓扑排序问题,拓扑排序是解决一个工程能否顺序解决的问题,本质是一个广度层次遍历的过程,通过记录顶点入度问题,进行逐步输出的工作.在实际生活中,往往是求解工程完成需要最短时间问题.比如生活 ...
- 汇智动力学院——Java 浅谈数据结构和算法
以前不管自己还是朋友在面试java工程师岗位的时候,都会被问到这样的问题: "介绍下java中的数据结构和算法", 很多朋友被问到的时候发现无从下口,甚至特别是一些初级java工程 ...
- 浅谈数据结构与算法分析学习及如何进行算法分析
一.前言 都说数据结构与算法分析是程序员的内功,想要理解计算机世界就不能不懂点数据结构与算法,然而这也备受争议,因为大多数的业务需求都用不上数据结构与算法,又或者说已经有封装好的库可以直接调用,例如J ...
最新文章
- 2017-09-29 前端日报
- Spring MVC环境中的文件上传功能实现
- tomcat苹果版安装步骤_mac系统安装apache tomcat配置方法图文详解
- hdu 5285(染色法判断二分图)
- js css加载器,webpack的CSS加载器的使用
- Ocelot-基于.NET Core的开源网关实现
- hdu5927Auxiliary Set
- CUPS-Centos6-dockerfile
- 深入理解JavaScript系列(31):设计模式之代理模式
- java中mergesort函数怎么用_MergeSort与TimSort,ComparableTimSort
- mysql定时导出数据_mysql定时备份数据
- 阿里代码检查p3c插件使用
- 前端实习小白日记—1
- 计算机原理图及接线图讲解,信号继电器的工作原理和作用以及接线图
- 【Scratch一级真题解析】电子学会等级考试一级(选择题)-2021年9月
- 调整DOSBox的窗口大小:跨过三连坑
- 『Consul』.NET Core快速接入Consul实现统一配置中心
- 《算法导论》习题5.3-1 ~ 5.3-7
- AndroidStudio开发的领养宠物app
- 教你个人邮箱怎么注册?126邮箱安全中心在哪里?
热门文章
- 深度学习环境配置_ubuntu18及以上
- 后端程序员必备:mysql数据库相关流程图/原理图芬芬细雨
- 自定义闪屏页广告倒计时view
- 再论意识、行为和结果
- 华为2288HV5使用U盘安装ESXI6.7.0
- 湖南计算机本科,湖南搞计算机科学与技术的本科有哪些?
- Ubuntu20.04部署微软counterfit AI系统安全测评工具实战
- “5G+区块链”护航新生儿转诊“生命通道”
- html5学习之多媒体播放
- (转)解密蚂蚁金服战略投资:赛道、条款与边界