zoukankan      html  css  js  c++  java
  • 笔试算法题(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是目前在无负权值图中对所有点求最短路径的最高效的算法;
  • 相关阅读:
    Linkerd 2.10(Step by Step)—将 GitOps 与 Linkerd 和 Argo CD 结合使用
    Linkerd 2.10(Step by Step)—多集群通信
    Linkerd 2.10(Step by Step)—使用 Kustomize 自定义 Linkerd 的配置
    Linkerd 2.10(Step by Step)—控制平面调试端点
    Linkerd 2.10(Step by Step)—配置超时
    Linkerd 2.10(Step by Step)—配置重试
    Linkerd 2.10(Step by Step)—配置代理并发
    本地正常运行,线上环境诡异异常原因集合
    Need to invoke method 'xxx' declared on target class 'yyy', but not found in any interface(s) of the exposed proxy type
    alpine 安装常用命令
  • 原文地址:https://www.cnblogs.com/leo-chen-2014/p/3756559.html
Copyright © 2011-2022 走看看