Dijkstra 算法——计算有权最短路径(边有权值)

【0】README

0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在理解 Dijkstra 的思想并用源代码加以实现;
0.2)最短路径算法的基础知识,参见 http://blog.csdn.net/pacosonswjtu/article/details/49894021
0.3) Dijkstra算法 涉及到的 优先队列的操作实现(该优先队列的数据类型不是 int , 而是 Distance),详情参见
http://blog.csdn.net/pacosonswjtu/article/details/49923389
0.4)Floyd算法(弗洛伊德算法)求的是: 两个顶点间的最短路径, 这个可以用Dijkstra算法来实现,因为 Dijkstra求的是某个顶点到其他顶点的最短路径,当然也就包括了 Floyd算法的求解情况;
0.5)所有点对最短路径

  • 0.5.1)有时重要的是要找出 图中所有顶点对之间的最短路径。 虽然我们可以运行|V| 次适当的单源算法, 但是如果要立即计算所有的信息, 我们还是期望有更快的算法, 特别是对稠密图的求解;
  • 0.5.2) 在第10章中, 我们将看到对赋权图求解这种问题的 一个 O(|V|^3)算法。虽然对于稠密图,它具有和运行 |V| 次简单 Dijkstra 算法相同的时间界, 但是循环是如此地紧凑以至于所有专门的点对算法很可能在实践中更快。当然,对于稀疏图更快的是运行 |V| 次用优先队列编写的 Dijkstra算法;

【1】Dijkstra 算法相关

1.1)贪婪算法一般分阶段去求解一个问题, 在每个阶段它都把当前出现的当做是最好的去处理:

  • 1.1.1)贪婪算法荔枝(使用最少数目的纸币找零钱):
    说找零钱, 大部分人首先数出面值1元的纸币,然后是面值5角的纸币、2角的纸币、1角的纸币等等;这种贪婪算法使用最少数目的纸币找零钱;
  • 1.1.2)贪婪算法的主要问题: 该算法不能总是成功,为了找还15角的零钱,如添加面值1元2角的纸币(这仅仅是举例说明)可破坏这种找零钱算法, 因为此时它给出的答案(一个面值1元2角的纸币+1个面值2角的纸币+一个面值1角的纸币==3个)不是最优的(1个面值1元的纸币+1个面值5角的纸币==2个);

1.2)Dijkstra 算法:解决单源最短路径问题的一般方法叫做 Dijkstra算法, 它的解法是贪婪算法最好的例子;

  • 1.2.1) Dijkstra 算法像无权最短路径算法一样, 按阶段进行;在每个阶段, 该算法选择一个顶点v, 它在所有未知顶点中具有最小的dv, 同时算法声明从s到v的最短路径是已知的。阶段的其余工作由dw值的更新工作组成;
  • 1.2.2)利用反证法证明得到, 只要没有边的值为负, 该算法总能够顺利完成,如果任何一边出现负值, 则算法可能得出错误的答案;
  • 1.2.3) Dijkstra算法描述(转自天勤计算机考研高分笔记——数据结构)
    设有两个顶点集合S 和 T, 集合S中存放图中已找到最短路径的顶点,集合T存放图中剩余顶点。初始状态时, 集合S 中只包含源点V0, 然后不断从集合T中选取到顶点V0 路径长度最短的顶点Vu 并将其并入到集合S中。集合S每并入一个新的顶点Vu, 都要修改顶点V0到 集合T中顶点的最短路径长度值。不断重复这个过程, 直到集合T的顶点全部并入到 S中为止;

Attention)在理解“集合S每并入一个新的顶点Vu,都要修改顶点V0到集合T中顶点的最短路径长度值”的时候需要注意:

  • A1)在Vu被选入S中后, Vu被确定为最短路径上的顶点, 此时Vu就像V0到达T中顶点的中转站 ,多了一个中转站, 就会多一些达到T中顶点的新路径,而这些新路径有可能比之前V0到T中顶点的路径还要短,因此需要修改原有V0到T中其他顶点的路径长度。此时对于T中的一个顶点Vk, 有两种情况:一种是V0不经过Vu 到达Vk的路径长度为a, 另一个是V0经过Vu到达Vk的长度为b。 如果a<=b, 则什么也不做;如果 a>b , 则用b来代替a。 用同样的方法处理T中其他顶点, 当T中所有顶点都被处理完后, 会出现一组新的 V0到T中各个顶点的路径,这些路径中有一条最短的, 对应了T中一个顶点, 就是新的 Vu, 将其并入S。重复上述过程, 最后T中所有的顶点都会被并入到S中, 此时就可以得到 V0到图中所有顶点的最短路径;

【2】Dijkstra算法实现

2.1)图是稠密的: 通过使用扫描表来找出最小值dv, 那么每一步将花费 O(|V|)时间找到最小值, 从而整个算法过程将花费 O(|V|^2)时间查找最小值;每次更新dw的时间是常数, 而每条边最多有一次更新,总计为 O(|E|),因此总的运行时间为
O(|E| + |V|)=O(|V|^2);
2.2)图是稀疏的:边数 |E|=Θ(|V|) , 那么扫描法就太慢了,不适用于稀疏图;

  • 2.2.1)一种处理方法是把更新处理成 DecreaseKey 操作: 此时, 查找最小值的时间为 O(log|V|), 即为执行那些更新的时间, 它相当于执行那些 DecreaseKey操作的时间。由此得出运行时间为 O(|E|log|V| + |V|log|V|)=O(|E|log|V|),它是对前面稀疏图的界的改进;由于优先队列不是有效地支持 Find操作, 由此 di 的每个值在优先队列的位置将需要保留并当 di 在优先队列中改变时更新。如果优先队列使用二叉堆实现的 话,那么将会很难办;如果使用配对堆(pairing heap, 见第12章),则程序不会太差;
  • 2.2.2)另一种方法是在每次执行第9行时把w和新值dw插入到优先队列中去。(这里仅仅提供了一个idea,可以不去细究,因为Solution多种多样)这样,在优先队列中的每个顶点就可能有多于一个的代表。当 DeleteMin操作吧最小的顶点从优先队列中删除时, 必须检查以肯定它不是已经知道的。这种方法虽然从软件观点来看是优越的,而且编程容易得多,但是,队列的大小可能达到 |E| 那么大。由于|E| <= |V|^2 意味着 log|E| <=2log|V| , 因此这并不影响渐进时间界。这样,我们仍然得到一个O(|E|log|V|)算法。不过,空间需求的确增加了, 在某些应用中这可能是严重的。不仅如此, 因为该方法需要 |E| 次而不仅仅是 |V| 次 DeleteMin, 所以在实践中运行很慢;
  • 2.2.3)图在大多数情况下都是非常稀疏的:注意,对于一些诸如计算机邮件和大型公交传输的典型问题, 它们的图都是非常稀疏的, 因为大多数顶点只有少数几条边。因此,在许多应用中 使用优先队列来解决这种问题 是很重要的;
  • 2.2.4)使用斐波那契堆实现 Dijkstra算法, 如果使用不同的数据结构,那么 Dijkstra算法可能会有更好的时间界。我们将看到另外的优先队列数据结构,叫做斐波那契堆(Fibonacci heap)。使用这种数据结构的运行时间为 O(|E| + |V|log|V|)。斐波那契堆具有良好的 理论时间界,不过,它需要相当数量的系统开销。因此,尚不清楚在实践中是否使用 斐波那契堆比使用具有二叉堆的Dijkstra 算法更好;

【3】看个荔枝:

这里写图片描述
这里写图片描述


【4】source code + printing results

Attention)

  • A1)代码的打印结果 和 手动模拟结果做个比较,以验证我的代码可行性: 注意将我的打印结果和章节【3】中的“有权最短路径Dijkstra算法步骤解析”中的各个步骤的binary heap 和 table内容(存储在进行Dijkstra算法过程中的节点相关数据)做个比较,很直观地演示了 Dijkstra算法的步骤;
  • A2)出现的问题: 本源代码用到了 优先队列(二叉堆)来选取最小的 distance所在的vertex编号,很方便,不过有个问题就是,当起始顶点(我们这里是v1)到后面的邻接顶点比之前的邻接顶点还要小(章节【3】中的v3被声明为已知后,v6的Distance更新为8,就是这种情况),那么就需要更新优先队列里面的v6的distance(由9更新为8),但是优先队列对于 find 操作不是很有效。
  • A3)如何解决优先队列对find操作不是很有效的情况: 这里, 我们引入了另一个int类型的数组indexOfVertexInHeap who stores index of vertexs in heap and let every element be -1 initially;比如,v6存放在 heap的第5个位置上,那么 indexOfVertexInHeap[6]=5,对的,就是这样sample, 后面,我们需要更新 heap里面的某个vertex的distance,直接用 indexOfVertexInHeap 导出该vertex在heap中的位置,然后直接更新就可以了,Bingo!

4.1)download source code:
Dijkstra算法源代码(优先队列实现):https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/p228_dijkstra
4.2)source code at a glance(for complete code, please click given link above):

#include "dijkstra.h"//allocate the memory for initializing unweighted table
WeightedTable *initWeightedTable(int size)
{   WeightedTable* table;int i;table = (WeightedTable*)malloc(sizeof(WeightedTable) * size);if(!table){Error("out of space ,from func initWeightedTable");return NULL;}for(i = 0; i < size; i++){table[i] = makeEmptyWeightedTable();        if(!table[i])return NULL;}return table;
} // allocate the memory for every element in unweighted table  
WeightedTable makeEmptyWeightedTable()
{WeightedTable element;element = (WeightedTable)malloc(sizeof(struct WeightedTable));if(!element){Error("out of space ,from func makeEmptyWeightedTable");return NULL;}   element->known = 0; // 1 refers to accessed , also 0 refers to not accessedelement->distance = MaxInt;element->path = -1; // index starts from 0 and -1 means the startup vertex unreaches other vertexsreturn element;
}// allocate the memory for storing index of  vertex in heap and let every element -1
int *makeEmptyArray(int size)
{int *array;int i;array = (int*)malloc(size * sizeof(int));if(!array){Error("out of space ,from func makeEmptyArray");return NULL;}       for(i=0; i<size; i++)array[i] = -1;return array;
}//computing the unweighted shortest path between the vertex under initIndex and other vertexs
void dijkstra(AdjTable* adj, int size, int startVertex, BinaryHeap bh)
{       int adjVertex;  int tempDistance;WeightedTable* table;int vertex;     AdjTable temp;  Distance tempDisStruct;int *indexOfVertexInHeap;int indexOfHeap;table = initWeightedTable(size);        tempDisStruct = makeEmptyDistance();indexOfVertexInHeap = makeEmptyArray(size);tempDisStruct->distance = table[startVertex-1]->distance;tempDisStruct->vertexIndex = startVertex-1;insert(tempDisStruct, bh, indexOfVertexInHeap); // insert the (startVertex-1) into the binary heap  table[startVertex-1]->distance = 0;// update the distance table[startVertex-1]->path = 0;// update the path of starting vertexwhile(!isEmpty(bh)){//vertex = deQueue(queue); // if the queue is not empty, conducting departing queue vertex = deleteMin(bh)->vertexIndex; // return the minimal element in binary heaptable[vertex]->known = 1; // update the vertex as accessed, also responding known 1temp = adj[vertex]->next;while(temp){adjVertex = temp->index; // let each adjVertex adjacent to vertex enter the queue//enQueue(queue, adjVertex); tempDistance = table[vertex]->distance + temp->weight; // update the distanceif(tempDistance < table[adjVertex]->distance){table[adjVertex]->distance = tempDistance;table[adjVertex]->path = vertex; //update the path of adjVertex, also responding path evaluated as vertex                           // key, we should judge whether adjVertex was added into the binary heap                //if true , obviously the element has been added into the binary heap(so we can't add the element into heap once again)if(indexOfVertexInHeap[adjVertex] != -1) {indexOfHeap = indexOfVertexInHeap[adjVertex];bh->elements[indexOfHeap]->distance = tempDistance; // update the distance of corresponding vertex in binary heap}else {tempDisStruct->distance = table[adjVertex]->distance;tempDisStruct->vertexIndex = adjVertex;insert(tempDisStruct, bh, indexOfVertexInHeap); // insert the adjVertex into the binary heap}}            temp = temp->next;      }       printDijkstra(table, size, startVertex);        printBinaryHeap(bh);printf("\n");}       printf("\n");
} //print unweighted table
void printDijkstra(WeightedTable* table, int size, int startVertex)
{int i;  char *str[4] = {"vertex","known","distance","path"};printf("\n\t === storage table related to Dijkstra alg as follows: === ");  printf("\n\t %6s%6s%9s%5s", str[0], str[1], str[2], str[3]);    for(i=0; i<size; i++){       if(i != startVertex-1 && table[i]->path!=-1) printf("\n\t %-3d   %3d   %5d      v%-3d  ", i+1, table[i]->known, table[i]->distance, table[i]->path+1);else if(table[i]->path == -1)printf("\n\t %-3d   %3d   %5d      %-3d  ", i+1, table[i]->known, table[i]->distance, table[i]->path);elseprintf("\n\t *%-3d  %3d   %5d      %-3d  ", i+1, table[i]->known, table[i]->distance, 0);}    
}int main()
{ AdjTable* adj;  BinaryHeap bh;int size = 7;int capacity;int i;int j;int column = 4;int startVertex;int adjTable[7][4] = {{2, 4, 0, 0},{4, 5, 0, 0},{1, 6, 0, 0},{3, 5, 6, 7},{7, 0, 0, 0},{0, 0, 0, 0},{6, 0, 0, 0}};int weight[7][7] = {{2, 1, 0, 0},{3, 10, 0, 0},{4, 5, 0, 0},{2, 2, 8, 4},{6, 0, 0, 0},{0, 0, 0, 0},{1, 0, 0, 0}};printf("\n\n\t ====== test for dijkstra alg finding weighted shortest path from adjoining table ======\n");adj = initAdjTable(size);       printf("\n\n\t ====== the initial weighted adjoining table is as follows:======\n");for(i = 0; i < size; i++)for(j = 0; j < column; j++) if(adjTable[i][j])          insertAdj(adj, adjTable[i][j]-1, i, weight[i][j]); // insertAdj the adjoining table overprintAdjTable(adj, size);capacity = 7;bh = initBinaryHeap(capacity+1);//conducting dijkstra alg to find the unweighted shortest path starts   startVertex = 1; // you should know our index for storing vertex starts from 0dijkstra(adj, size, startVertex, bh);return 0;
} 

4.3)printing results:

这里写图片描述
这里写图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331881.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring使用自定义注解_用Spring组成自定义注释

spring使用自定义注解Java批注在2004年随Java 5一起引入&#xff0c;是一种将元数据添加到Java源代码中的方法。 如今&#xff0c;许多主要框架&#xff08;如Spring或Hibernate&#xff09;都严重依赖注释。 在本文中&#xff0c;我们将介绍一个非常有用的Spring功能&#xf…

打印结果和调试结果不一样(C语言)

【0】README 0.1&#xff09;本文旨在阐述 个人的debug经历&#xff0c;遇到的各种debug 奇葩问题&#xff0c; 说是奇葩&#xff0c;其实也是自己 不小心或者说是编程习惯不好&#xff1b; 【1】debug和running的运行结果不一致&#xff08;乍眼一看&#xff0c;你肯定醉了&a…

mysql add default_MySQL中create table DEFAULT 用法

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)][table_options] [select_statement]TEMPORARY&#xff1a;该关键字表示用create table新建的表为临时表&#xff0c;此表在当前会话结束后将自动消失。临时表主要被应用于存储过程中&#xff0c;…

jakarta ee_Jakarta EE贡献–入门

jakarta ee您是否有兴趣帮助Jakarta EE向前发展&#xff1f; 我也是。我想提供一些详细信息&#xff0c;以帮助有兴趣入门的人。 第1步&#xff1a; 开始捐款的第一步是签署Eclipse Foundation Committer and Contributor Agreement&#xff08;ECA&#xff09;&#xff1a; …

最小生成树基础

【0】README 0.1&#xff09; 本文总结于 数据结构与算法分析&#xff0c; 源代码均为原创&#xff0c; 旨在 review 最小生成树的基础知识&#xff1b; 0.2&#xff09;了解本文的内容是 分析 Prim算法&#xff08;普利姆算法&#xff09;和 Kruskal算法&#xff08;克鲁斯卡…

mysql dump gtid_mysqldump命令详解 Part 3- 备份全库

前面说了MySQL Linux平台和Windows平台的安装下面开始是MySQL的一些学习笔记前面我们说了如果构造数据这节开始说MySQL 的备份环境为MySQL 5.7.25在解释命令之前我们先弄清楚数据库中有哪些对象上一节我们建立了数据库并建立相关的对象数据库表存储过程函数触发器事件这节讲一些…

apache lucene_Apache Lucene中的并发查询执行

apache luceneApache Lucene是一个出色的并发纯Java搜索引擎&#xff0c;如果您愿意&#xff0c;它可以轻松地使服务器上的可用CPU或IO资源饱和。 “典型” Lucene应用程序的并发模型在搜索时每个查询一个线程&#xff0c;但是您是否知道Lucene也可以使用多个线程同时执行一个查…

最小生成树——Prim(普利姆)算法

【0】README 0.1&#xff09; 本文总结于 数据结构与算法分析&#xff0c; 源代码均为原创&#xff0c; 旨在 理解Prim算法的idea 并用 源代码加以实现&#xff1b; 0.2&#xff09;最小生成树的基础知识&#xff0c;参见 http://blog.csdn.net/pacosonswjtu/article/details…

mysql grant usage on_grant 权限 on 数据库对象 to 用户

grant 权限 on 数据库对象 to 用户一、grant 普通数据用户&#xff0c;查询、插入、更新、删除 数据库中所有表数据的权利。grant select on testdb.* to common_user’%’grant insert on testdb.* to common_user’%’grant update on testdb.* to common_user’%’grant del…

openjdk8 项目结构_OpenJDK织机和结构化并发

openjdk8 项目结构Project Loom是Hotspot Group赞助的项目之一&#xff0c;旨在向JAVA世界提供高吞吐量和轻量级的并发模型。 在撰写本文时&#xff0c;Loom项目仍在积极开发中&#xff0c;其API可能会更改。 为什么要织机&#xff1f; 每个新项目可能会出现的第一个问题是为什…

mysql连库串_数据库连接串整理 - osc_ac5z111b的个人空间 - OSCHINA - 中文开源技术交流社区...

常用JDBC驱动与连接字符串MySQLdriver&#xff1a;com.mysql.jdbc.Driverurl&#xff1a;jdbc:mysql://localhost:3306/mydbMySQL url格式&#xff1a;jdbc:mysql://[host:port]/[database][?参数名1][参数值1][&参数名2][参数值2]…参数名称参数说明缺省值最低版本要求us…

最小生成树——Kruskal(克鲁斯卡尔)算法

【0】README 0.1&#xff09; 本文总结于 数据结构与算法分析&#xff0c; 源代码均为原创&#xff0c; 旨在 理解 Kruskal&#xff08;克鲁斯卡尔&#xff09;算法 的idea 并用 源代码加以实现&#xff1b; 0.2&#xff09;最小生成树的基础知识&#xff0c;参见 http://blo…

java 正则表达式 开头_如何在Java中修复表达式的非法开头

java 正则表达式 开头您是否遇到过这个令人难以置信的错误&#xff0c;想知道如何解决它&#xff1f; 让我们仔细阅读一下&#xff0c;研究如何解决表达式Java非法开头错误。 这是一个动态错误&#xff0c;这意味着编译器会发现某些不符合Java编程规则或语法的内容。 初学者大…

php mysql数据备份命令_MySQL数据备份与恢复的相关操作命令

将mysql安装目录设置到系统环境变量中, 方便在命令行终端直接执行.linux下mysql安装后, root默认密码为空, 可直接执行mysql 登录将mysql安装目录设置到系统环境变量中, 方便在命令行终端直接执行.linux下mysql安装后, root默认密码为空, 可直接执行mysql 登录.正常登录命令mys…

DFS——深度优先搜索基础

【0】README 0.1&#xff09; 本文总结于 数据结构与算法分析&#xff0c; 源代码均为原创&#xff0c; 旨在 review DFS——深度优先搜索 的基础知识&#xff1b; 【1】深度优先搜索的应用 1.1&#xff09;深度优先搜索算法描述&#xff08;转自天勤计算机考研高分笔记——数…

rest post put_REST / HTTP方法:POST与PUT与PATCH

rest post put每个HTTP请求都包含一个方法 &#xff08;有时称为verb &#xff09;&#xff0c;该方法指示对标识的资源执行的操作。 在构建RESTful Web服务时&#xff0c;HTTP方法POST通常用于创建资源&#xff0c;而PUT用于资源更新。 尽管在大多数情况下这很好&#xff0c;…

回归模型的score得分为负_深度研究:回归模型评价指标R2_score

回归模型的性能的评价指标主要有&#xff1a;RMSE(平方根误差)、MAE(平均绝对误差)、MSE(平均平方误差)、R2_score。但是当量纲不同时&#xff0c;RMSE、MAE、MSE难以衡量模型效果好坏。这就需要用到R2_score&#xff0c;实际使用时&#xff0c;会遇到许多问题&#xff0c;今天…

DFS应用——遍历无向图

【0】README 0.1&#xff09; 本文总结于 数据结构与算法分析&#xff0c; 源代码均为原创&#xff0c; 旨在 理解 如何对无向图进行深度优先搜索 的idea 并用源代码加以实现&#xff1b; 0.2&#xff09; 本文还引入了 背向边&#xff08;定义见下文描述&#xff09;&#x…

高效的磁力搜索引擎 -_高效的企业测试-结论(6/6)

高效的磁力搜索引擎 -该系列的最后一部分将涵盖其他端到端测试&#xff0c;生产中的测试以及各部分的结论。 进一步的端到端测试和生产中的测试 除了仅验证单个被测应用程序并模拟外部问题的系统测试之外&#xff0c;我们的管道还必须包括完整的端对端测试&#xff0c;以验证…

linux安装mysql phpmyadmin_ubuntu mysql远程连接+phpmyadmin安装

一、如何让ubuntu上的mysql允许远程连接进入MySQL&#xff0c;执行如下命令:use mysql;GRANT ALL PRIVILEGES ON *.* TO username% IDENTIFIED BY password WITH GRANT OPTION;flush privileges; //刷新select host,user from user; //查看是否成功退出mysql&#xff1b;打开su…