最短路径算法--无权最短路径

来源:互联网 发布:扫描服务器开放的端口 编辑:程序博客网 时间:2024/05/16 14:19

简介

  输入是一个赋权图:与每条边(vi,vj)相联系的是穿越该弧的代价(或称为值)ci,j。一条路径v1v2v3…vN的值是,叫做赋权路径长(weighted path length),而无权路径长(unweighted path length)只是路径上的边数,即N-1。


单源路径问题

  给定一个赋权图G=(V,E)和一个特定顶点s作为输入,找出从s到G中每一个其它顶点的最短赋权路径。

  负值圈(negative-cost cycle):带圈图中圈中的权值有为负值。当它出现在图中时,最短路径问题就是不确定的。


无权最短路径

下图是一个无权图G,使用某个顶点s作为输入参数,我们想要找出从s到所有其它顶点的最短路径。我们只对包含在路径中的边数有兴趣,因此在边上不存在权。显然,这是赋权最短路径问题的特殊情形,因为我们可以为所有的边都赋以权1。一个无权有向图G:


设我们选择s为v3。此时可以说出从s到v3的最短路径是长为0的路径。通过考察与s邻接的那些顶点依次类推找出长为1,2,3的路径。把这个信息做个标记,如下:


这种搜索图的方法称为广度优先搜索(breadth-first search)。该方法按层处理顶点:距开始点最近的那些顶点首先被求值,而最远的那些顶点最后被求值。这很像对树的层序遍历(level-order traversal)

用于无权最短路径计算的表的初始配置:


首先,把从s开始到顶点的距离放到dv栏中。开始的时候,除s外所有的顶点都是不可达的,而s的路径长为0。pv栏中的项为簿记变量,它将使我们能够显示出实际的路径。known中的项在顶点被处理以后置为true。最初,所有的顶点都不是known(已知)的,包括开始顶点。当一个顶点被标记为known时,我们就有了不会再找到更便宜的路径的保证,因此对该顶点的处理实质上已经完成。

无权最短路径算法的伪代码:

    void unweighted(Vertex s){    for each Vertex v{    v.dist = INFINITY;//无穷    v.known = false;    }    s.dist = 0;    for(int currDist = 0;currDist < NUM_VERTICES;currDist++){    for each Vertex v     if(!v.known && v.dist == currDist){     v.known = true;     for each Vertex w adjacent to v     if(v.dist==INFINITY){     w.dist = currDist + 1;     w.path = v;     }     }    }        }
由于双层嵌套for循环,因此该算法的运行时间为O(|V|2)。一个明显的低效之处在于尽管所有的顶点早就成为known了,但是外层循环还是要继续,直到NUM_VERTICES-1为止。虽然额外的附加测试可以避免这种情形发生,但是它并不能影响最坏情形运行时间,在以点v9作为起点的下图中作为输入时,无权最短路径算法的坏情形:

我们可以用非常类似于对拓扑排序所做的那样来排除这种低效性。在任一时刻,只存在两种类型的dv不等于无穷的unknown顶点,一些顶点的dv=currDist,而其余的则有dv=currDist+1。由于这种附加的结构,因此搜索整个的表以找出合适的顶点的做法是非常浪费的。

  一种非常简单抽象的解决方案是保留两个盒子。1号盒将装有dv=currDist的那些未知顶点,而2号盒则装有dv=currDist+1的那些顶点。找出一个合适顶点的测试可以用查找1号盒内的任意顶点代替。在更新w(内层if语句块的内部)以后,我们可以把w加到2号盒中。在外层for循环终止以后,1号盒是空的,而2号盒则可转换成1号盒以进行下一趟for循环。

  我们可以使用一个队列把这种想法进一步精化。在迭代开始的时候,队列只含有距离为currDist的那些顶点。当添加距离为currDist+1的那些邻接顶点时,由于他们自队尾入队,因此这就保证它们直到所有距离为currDist的顶点都被处理之后才被处理。在距离currDist处的最后一个顶点出队并被处理之后,队列只含有距离为currDist+1的顶点。因此该过程将不断进行下去。我们只需要把开始的节点放入队列中以启动这个过程即可。

无权最短路径算法的伪代码:

    void unweighted(Vertex s){    Queue<Vertex> q = new Queue<Vertex>();    for each Vertex v{    v.dist = INFINITY;    }    s.dist = 0;    q.enqueue(s);        while(!q.isEmpty()){    Vertex v = q.dequeue();    for each Vertex w adjacent to v{    if(w.dist==INFINITY){    w.dist = v.dist+1;    w.path = v;    q.enqueue(w);        }    }    }            }

我们已经假设开始顶点s是作为参数被传递的。再有,如果某些顶点从开始节点出发是不可达的,那么有可能队列会过早地变空。在这种情况下,将对这些节点报出INFINITY(无穷)距离,这是完全合理的。最后known域没有使用;一个顶点一旦被处理它就从不再进入队列,因此它不需要重新处理的事实就意味着被做了标记。这样一来,known域可以去掉。

无权最短路径算法期间数据变化情况(该图还包括对known发生的变化):


使用与对拓扑排序进行同样的分析,我们看到,只要使用邻接表,则运行时间就是O(|E|+|V|)。




0 0