当前位置: 首页 > news >正文

笔试算法题(49):简介 - 图最短路径算法

图最短路径算法(Graph Shortest Path Algorithm, eg: Floyd-Warshall, Dijkstra, Bellman-Ford, SPFA, Kruskal, Prim, Johnson)

  • 最短路径问题有多个衍生问题(并且每个衍生问题都涉及是否有负权边)

    单源点最短路径

    单终点最短路径

    单对顶点最短路径

    任意顶点间最短路径

Floyd-Warshall Algorithm

  • 适用于多源,可有负权边的有向图的最短路径;时间复杂度为O(V^3),空间复杂度为O(V^2);

  • 二维数组path[i][j]表示顶点i到顶点j之间的代价(初始化时由于没有探测他们之间的代价关系,所以为无限大);本算法采取的策略是穷举所有顶点对之间所有可能的中间顶点,并选择代价最小的作为最优解;

 1 procedure FloydWarshallWithPathReconstruction ()
 2  for k := 1 to n
 3    for i := 1 to n
 4      for j := 1 to n
 5        if path[i][k] + path[k][j] < path[i][j] then
 6           path[i][j] := path[i][k]+path[k][j];
 7           next[i][j] := k; //next[i][j]表示顶点i和顶点j的中间点
 8 
 9  procedure GetPath (i,j)
10  if path[i][j] equals infinity then
11     return "no path";
12  int intermediate := next[i][j];
13  if intermediate equals 'null' then
14     return " "; /* there is an edge from i to j, with no vertices between */
15  else
16     return GetPath(i,intermediate) + intermediate + GetPath(intermediate,j);

 

Dijkstra Algorithm

  • 适用于有向、无负权边图中,单个源点到其他所有顶点的最短路径问题(Single-Source Shortest Path Problem for Graph with Non-Negative Edge Path Costs)。给定一个带权重的有向图G,V表示所有点的集合,E表示所有边的集合,并且每条边具有权重值w,要求找到给定start点到V中所有其他点 的最短距离。在寻路过程中,如果发现一个新的顶点M,并且从源点到这一个顶点M再到前一个目标顶点的距离比之前的距离小,则更新这个目标定点的最小距离;

  • Dijkstra算法适用于稠密图(边多点少),时间复杂度:使用最小优先队列实现Extract_Min()函数的话,为O(V^2 + E);使用二叉堆实现Extract_Min()函数的话,为O(V^2);使用斐波那契堆(Fibonacci Heap)实现Extract_Min()函数的话,为O(V*lgV + E);Dijkstra算法的一个应用是OSPF(Open Shortest Path First,开放最短路径优先),网络路由寻址的实现

 1 function Dijkstra(G, w, s)
 2  for each vertex v in V[G] //初始化
 3     d[v] := infinity //d[v]存储其他顶点到起始点s的最短距离
 4     previous[v] := undefined //previous[v]存储所有顶点的前置节点
 5  d[s] := 0
 6  S := empty set
 7  Q := set of all vertices
 8  while Q is not an empty set // Dijkstra演算法主體
 9     u := Extract_Min(Q) //Extract_Min()一般使用最小堆实现O(logN)
10     S := S union {u}
11     for each edge (u,v) outgoing from u
12        if d[v] > d[u] + w(u,v) //根据当前节点u的d[u]更新其相邻节点的d[v]
13           d[v] := d[u] + w(u,v)
14           previous[v] := u

 

Bellman-Ford Algorithm

  • 适用于单源、可有负权边的有向图的最短路径,Bellman-Ford对每个顶点都只进行一次处理,所以可以处理负权边,而Dijkstra由于是根据边权值大小选择下一条边,所以负权边可能造成循环;此算法时间复杂度为O(VE),空间复杂度为O(V);

  • 数组V[k]表示所有顶点到source的最短距离(初始化为Infinite),并且使用数组P[h]记录所有顶点的前驱,用于记录最短路径;遍历所有 的边集合E内的边e,e连接顶点u和v,判断u和v之间是否可以组成最短路径,这样的遍历重复|V|次;由于V[k]中元素初始化为Infinite,所 以如果如果某个环内存在负权值边,则算法失败;

 1 procedure BellmanFord(list vertices, list edges, vertex source)
 2    // This implementation takes in a graph, represented as lists of vertices
 3    // and edges, and modifies the vertices so that their distance and
 4    // predecessor attributes store the shortest paths.
 5 
 6    // Step 1: initialize graph
 7    for each vertex v in vertices:
 8        if v is source then v.distance := 0
 9        else v.distance := infinity
10        v.predecessor := null
11 
12    // Step 2: relax edges repeatedly
13    for i from 1 to size(vertices)-1:
14        for each edge uv in edges: // uv is the edge from u to v
15            u := uv.source
16            v := uv.destination
17            if u.distance + uv.weight < v.distance:
18                v.distance := u.distance + uv.weight
19                v.predecessor := u
20 
21    // Step 3: check for negative-weight cycles
22    for each edge uv in edges:
23        u := uv.source
24        v := uv.destination
25        if u.distance + uv.weight < v.distance:
26            error "Graph contains a negative-weight cycle"

 

SPFA (Shortest Path Faster Algorithm)

  • 适用于单源、可有负权边的有向图;大多数时候当图存在负权边,Dijkstra不能使用,而Bellman-Ford时间复杂度过高,所以最好使用 SPFA;其实SPFA是Bellman-Ford的优化版本,时间复杂度为O(kE),K是一个远小于V的数字,表示所有顶点进入FQ队列的平均次数 (<=2),但是SPFA及其不稳定,并严重依赖于图数据;

  • SPFA的策略是只有那些在前一次的Relax中的最小距离减小的点,才检查他们的邻接节点的最小距离是否也可减小。SPFA并不会在每一个顶点的最短路 径更新之后就去更新与其相连的顶点,而是等所有顶点都处理完全之后再进行最短路径的更新,这样大大减少重复更改最短路径的次数;所以SFPA中顶点会多次 进入队列,SPFA适合用于稀疏图,此时其拥有最高的效率;

  • 数组D[k]存储所有其他顶点到Source的最短距离(初始化为Infinite);数组G[i][j]存储图中直接相连的顶点之间的距离;FIFO队 列Q存储需要进一步优化的顶点,每次从Q中取出顶点u,对每一个u直接相连的顶点v进行Relax操作,如果顶点v的最短距离改变了,并检查v没有在Q 中,则将v加入Q中,之后处理下一个与u直接相连的顶点;检查完u所有直接相连的顶点之后从Q中取出下一个顶点,并进行相同的处理;当Q为空的时候算法结 束,次数D[k]存储的就是图中每一个顶点到source的最短距离;

  • 根据将顶点插入到FIFO队列Q的位置不同,SPFA可有两种优化:Small Label First (SLF)和Large Label Last (LLL);但由于SPFA及其不稳定,所以一般情况都使用Dijkstra替代;

 1 void spfa(int start){
 2   int i,j;
 3   //初始化部分
 4   for (i=1;i<=n;++i){
 5     dist[i]=2147483647;
 6     inqueue[i]=0;
 7   }
 8   //将头节点入队
 9   dist[start]=0;
10   int h=0,t=1;
11   inqueue[start]=1;
12   queue[1]=start;
13   int now;
14   do{
15     h++;
16     now=Connect[queue[h];
17     inqueue[queue[h]=0;
18     while (now){
19       if (dist[Data[now].v]>dist[queue[h]+Data[now].w){
20         dist[Data[now].v]=dist[queue[h]+Data[now].w;
21         //进行松弛并扩展被松弛的点
22         if (!inqueue[Data[now].v]){
23           inqueue[Data[now].v]=1;
24           queue[++t]=Data[now].v;
25         }
26       }
27       now=Pre[now];
28     }
29   }while (h<t);
30  }

 

Johnson Algorithm

  • 为了让Dijkstra适用于具有负权值边的图,Johnson通过特殊方式将负权值图转换为正权值图,并且为所有顶点之间的最短路径;
  • 首先进行一次Bellman-Ford,然后利用等式W(i,j)=h[i]-h[j]+w(i,j);对原图进行重新标号(Re-wrighting, 其实是去除负权值边从而可以使用Dijkstra算法,h[]就是通过Bellman-Ford得到的路径标记,w[][]是边的原始权值);最后对每个 点使用Dijkstra。Johnson是目前在无负权值图中对所有点求最短路径的最高效的算法;

转载于:https://www.cnblogs.com/leo-chen-2014/p/3756559.html

相关文章:

  • 如何在 IIS6.0中部署asp.net mvc程序(转)
  • SCAU 18924 二叉树的宽度
  • SCAU 18724 二叉树的遍历运算
  • SCAU 18923 二叉树的直径
  • 关于JavaScript中整数数字不能直接调用方法
  • SCAU 发牌程序(java)
  • CSS flex布局 flex-grow不为1 items始终均匀分配剩余空间问题
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [Symbol.toPrimitive](hint) hint 什么时候为 default?
  • JavaScript 对象遍历方法及其遍历顺序的总结
  • vue 实现根据窗口大小自适应图片预览
  • 《计算机网络 自顶向下方法》笔记 - 第二章 应用层
  • 使用 BrowserRouter 报错 invaild hook call 解决方案
  • python中assert关键字的作用
  • CSS3 :nth-child(n)用法
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • gf框架之分页模块(五) - 自定义分页
  • HTTP请求重发
  • idea + plantuml 画流程图
  • JS基础之数据类型、对象、原型、原型链、继承
  • JS实现简单的MVC模式开发小游戏
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 对象管理器(defineProperty)学习笔记
  • 关键词挖掘技术哪家强(一)基于node.js技术开发一个关键字查询工具
  • 关于List、List?、ListObject的区别
  • 回顾 Swift 多平台移植进度 #2
  • 聊聊flink的TableFactory
  • 你真的知道 == 和 equals 的区别吗?
  • 前端学习笔记之观察者模式
  • 前嗅ForeSpider教程:创建模板
  • 算法---两个栈实现一个队列
  • nb
  • # Maven错误Error executing Maven
  • #stm32驱动外设模块总结w5500模块
  • #Z0458. 树的中心2
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (day 12)JavaScript学习笔记(数组3)
  • (java)关于Thread的挂起和恢复
  • (八十八)VFL语言初步 - 实现布局
  • (六)vue-router+UI组件库
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转)程序员技术练级攻略
  • (转载)Linux 多线程条件变量同步
  • * CIL library *(* CIL module *) : error LNK2005: _DllMain@12 already defined in mfcs120u.lib(dllmodu
  • .chm格式文件如何阅读
  • .net core 控制台应用程序读取配置文件app.config
  • .Net7 环境安装配置
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • .NET设计模式(11):组合模式(Composite Pattern)
  • .Net中的集合
  • .set 数据导入matlab,设置变量导入选项 - MATLAB setvaropts - MathWorks 中国
  • @Autowired 与@Resource的区别