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是目前在无负权值图中对所有点求最短路径的最高效的算法;
  • 相关阅读:
    stream to byte[], byte[] to srting
    内容输出为每行的字符串的方法
    .net面式题
    .net多站点通过StateServer实现session共享
    .net 数据绑定gridview 和Repeater 序号,Container.ItemIndex
    js实现table用鼠标改变td的宽度,固定table宽度和高度超过显示点
    .aspx、MasterPage、.ascx加载顺序
    IIS删除http header信息如Server, X-Powered-By, 和X-AspNet-Version
    基于Asp.net C#实现HTML转图片(网页快照)
    js获取页面宽度高度及屏幕分辨率
  • 原文地址:https://www.cnblogs.com/leo-chen-2014/p/3756559.html
Copyright © 2011-2022 走看看