如何计算图的最短路径?

算法导论(MIT 6.006 第15讲 第16讲 第17讲)算法

最短路径的定义是什么?

最短路径即拥有最小权重的路径p;
路径定义: p=<$v_0$,$v_0$,...,$v_k$>, 其中当$0\leq i<k$时,有 ($v_i$,$v_{i+1}$) $\in$ E;
路径的权重:w(p)=$\Sigma{^{k-1}_{i=0}}w(v_i,v_{i+1})$ ;网络

加上权重的数学表示方式

  1. 边存在权重的图:G(V,E,W) ,W是一个函数,做用于边,生成一个实数,即W(E)->R
  2. 顶点到自身的路径:($V_0$)表示从($V_0$)到($V_0$)的路径,权重是0
  3. 两个顶点之间的最短路径:
    $\delta(u,v)=\lbrace{^{min\lbrace w(p)\rbrace{\quad} u,v之间存在路径}_{\infty{\qquad}u,v以前不存在路径}} $
E与V的关系 E=O($V^2$ )。对于有向图来说,假设有两个顶点,v1,v2,他们之间只有4种链接状况,依次类推

为何会有负的权重?

好比社交网络上的喜欢能够看作是正的权重,比喜欢能够看作是负的权重函数

负权重的边带来什么问题?

若是存在一个带有负权重的边,那么每通过一个循环,会减小原有的权重值,这样形成的现象是能够获得任何能够获得的权重值。好比路径p=<S,A>权重是4,可是路径p=<S,A,C,B,A>权重是3spa

图片描述

最短路径算法的通常思路是什么?

d(v) 表示从源点s到当前节点v的路径权重 ,$\pi[v]$表示当前最好的路径上,v的前一个节点 ,经过这种方式就能重构整个最短路径

针对没有负权重的环code

  1. 初始化 d[v] = $\infty$ ,$\pi[u]$=NIL,d[s]=0
  2. 经过某种方式选择边(u,v),执行Relax操做,去更新源点到选择的顶点的当前路径值,以及选择顶点的前一个节点
Relax(u,v,w):
    select edge(u,v):
        if d[v]>d[u]+w(u,v):
            d[v]=d[u]+w(u,v)
            PI[v]=u
    until all edges have d[v] <= d[u]+w(u,v)

relax操做的过程当中会不会产生一个一个比 $\delta$(s,v)还要小的值?

经过概括法,假设有 d[u] $\geq$ $\delta$(s,u)。已知的是$\delta(s,v)$表示s到v的最短路径,那么任意一个到v的顶点u和源点s到u的最短路径一定大于等于$\delta(s,v)$,也就是
$$\delta(s,v)\leq\delta(s,u)+w(u,v)$$
经过前面的假设,则一定有 $\delta(s,v)\leq d[u]+w(u,v)=d[v]$ 。这说明,中间的过程的任意一个阶段产生的结果d[v]都不会比$\delta$(s,v)还要小排序

最短路径算法的通常思路问题一:错误的选边致使复杂度为指数级别

构造以下结构的图队列

边的权值按照$2^{n/2}$方式分配,图中给出的6个点的示例,若是所有显示的边($v_0$,$v_2$)的权值为$2^{n/2}$,并依次递减到1

图片描述
假设源点为$v_0$,初始化选择的路径以下,能够获得从源点到各个点的路径长度。图片

图片描述

此时,Relax($v_4$,$v_6$)的边,会更新$v_0$到$v_6$的路径长度为13ci

图片描述
再Relax($v_2$,$v_4$)的边,会更新$v_0$到$v_4$的路径长度为10get

图片描述
因为新$v_0$到$v_4$的路径长度变短,那么($v_0$,$v_5$)的路径会变短为11

图片描述
这个时候有可能先选的执行Relax的边是 ($v_5$,$v_6$),那么($v_0$,$v_6$)的路径会变短为12

图片描述
再次Relax边($v_4$,$v_6$),那么($v_0$,$v_6$)的路径会变短为11

图片描述
针对有n个顶点的状况:

  • 首先Relax($v_{n-2}$,$v_{n}$),使得d[$v_n$]减1
  • 再Relax($v_{n-4}$,$v_{n-2}$),使得d[$v_{n-2}$]减2
  • 而后Relax($v_{n-2}$,$v_{n-1}$)和($v_{n-1}$,$v_{n}$)使得d[$v_n$]减1
  • 再执行Relax($v_{n-2}$,$v_{n}$),使得d[$v_n$]减1

可发现,当Relax的边($v_{n-2}$,$v_{n}$)权重为1的时候,使得顶点d($v_n$)减1;当Relax边($v_{n-4}$,$v_{n-2}$)权重为2的时候,使得顶点d($v_n$)减2,也就是从权重按照 1,2,4,...,$2^{(n/2)-1}$,$2^{(n/2)}$的方式执行的过程当中,d($v_n$)须要执行减小的总次数为1+2+4+...+$2^{(n/2)}$=$2^{(n/2)}-1$,也就是说,会执行的次数为指数级别

最短路径算法的通常思路问题二:负权重环

若是在源点到目标节点通过的路径上,通过环会致使权重减小,这个算法不会结束

如何获取有向无环图(DAG)中,单个源点到某个点的最短路径?

DAG表示只是没有环,能够存在负边权重
  1. 对DAG进行拓扑排序,这样保证了u到v的路径必定是u在v以前
  2. 找到源点,按照从左到右,DAG排列的顺序,对通过的每一个顶点进行Relax操做,便获得了源点到全部顶点的最短路径

假设排序好的拓扑图以下,对于初始化时,每一个源点到每一个节点的距离都认为是 $\infty$

图片描述
第一步从源点往下走,找到它的全部的边,对边执行Relax操做

图片描述
源点执行完毕,而后按照拓扑排列的顺序,从左往右执行,因为Relax只更改小于的状况,所以只有最后两个节点的路径值被更新

图片描述
继续往右执行Relax

图片描述

继续往右执行Relax

图片描述

至此执行完毕,能够看到源点到全部节点的最短路径,从左到右分别是 $\infty$,0,2,6,5,3

若是图中有环,可是通过这个环不会致使权重减小,如何计算最短路径?

使用Dijkstra算法。伪代码算法以下:

Dijkstra(G,w,s): //G是图,w是权值,s是源点
    Initialize(G,s) // 初始化,设置d[s]=0,其它都是无穷,以及PI
    S <- {}    //已知最短路径的点的集合
    Q <- V[G]  //须要被处理的顶点,能够看作是一个最小优先级队列,根据d()值进行排序
    while Q is not empty: //只要还有没处理的节点
        u <- Extract-Min(Q) //从节点中找出一个最小的路径权重的节点,并从Q中移除
        S <- S U {u} //将找到的节点并到S中
        for each vertex v belong to Adj
            Relax(u,v,w) //对边的d()值进行更新

例子以下,选择A为源点

图片描述

  1. 进行初始化,从A到其它节点的距离都是$\infty$,即 S={} ,Q={A(0),B($\infty$),C($\infty$),D($\infty$),E($\infty$)};
  2. 获取队列中的最小值,此时是A自己,此时S={A(0)},而后进行一次Relax操做,即发现A能达到的顶点为B,C,更新后队列中的值为 Q={B(10),C(3),D($\infty$),E($\infty$)};
  3. 获取队列中的最小值,此时是C,S={A(0),C(3)},对选择的C作Relax,C能到达的节点为B,D,E,相应队列更新为:Q={B(7),D(11),E(5)};
  4. 获取队列的最小值,此时是E,S={A(0),C(3),E(5)},对选择的E作Relax,E能到的节点为D,因为比现有的D值要大,因此没有更新,Q={B(7),D(11)};
  5. 获取队列的最小值,此时是B,此时S={A(0),C(3),E(5),B(7)},B能到达的只剩下D了,B到D获得的值为9,要小,更新Q={D(9)}
  6. 获取队列最小的值,此时是D,此时S={{A(0),C(3),E(5),B(7),D(9)},至此结束。
括号中的值表示路径距离

Dijkstra算法的时间复杂度

全部的耗时操做包括:

  • 将全部的顶点插入优先级队列中,耗时为 $\theta(V)$;
  • 从优先级队列中提取一个最小的值,耗时为$\theta(V)$;
  • Relax操做对边进行d值减小,耗时为$\theta(E)$;

实现优先级队列方式不一样,耗时不一样

  1. 使用Array。 提取最小值花销:$\theta(V)$,Relax对d值进行减小$\theta(1)$,操做全部的队列中的元素,那么时间就是 $\theta(V*V+E*1)$=$\theta(V^2)$
  2. 使用最小堆。提取最小值花销:$\theta(\lg V)$,减小key的花销$\theta(\lg V)$,操做全部的队列中的元素,那么时间就是 $\theta(V*\lg V+E*\lg V)$
  3. 使用Fibonacci堆,提取最小值花销:$\theta(\lg V)$,减小key的花销$\theta(1)$,能到达$\theta(V*\lg V+E)$
最直观的使用Dijkstra的感觉是:如下图为例: 图片描述
假设绿色的点是源点,若是用这样长度的绳子将各个节点链接起来,那么拎起绿色的球,从上往下悬挂,那些蹦直的线相加就是源点到各个点的最短距离,好比绿色是源点,到其它点的最短距离分别是 7,12,18,22(颜色依次是紫色、蓝色、黄色、红色)

为何Dijkstra不能处理负权重环的问题?

Dikstra不会去看已经处理好的节点,只会处理没有看到的节点,若是已经处理的节点都是最小的值,再不存在负权重环的状况下,是不会出现使得路径变小的状况。详见:https://stackoverflow.com/que...

若是在源点到目标节点通过的路径上,有通过环且会致使权重减小,怎么处理最短路径问题?

使用Bellman-Ford算法。

Bellman-Ford(G,w,s):
    Initialuze(G,s)
    for i=1 to |V|-1:
        for each edge(u,v) belong to E:
            Relax(u,v,w)
    for each edge (u,v) belong to E:
        if d[v]>d[u]+w(u,v)
            report negative cycle exist

Bellman-Ford最终提供的是,若是没有负权重的环,那么能返回最短路径(d[v]=$\delta(s,v)$),不然只是检测出存在负权重的环

耗时分析

两个for循环,分别为V,E,因此时间复杂度就是O(VE)

为何Bellman-Ford算法在不存在负权重环的状况下可以计算最小路径?

只须要证实,若是不存在负权重的环,那么通过Bellman-Ford有d[v]=$\delta(s,v)$。

取一条拥有最少边的最短路径p=<$v_0$,$v_1$,...,$v_k$>,其中$v_0$为s,$v_k$=v。 若是不存在负权重的环,那么说明p是一条简单路径,这代表,k$\leq$|V|-1。

这里也不多是一个正环,即每通过这个环,权重增长,若是是那么它就不是最短路径了

当进行第一次循环的时候,取到的边($v_0$,$v_1$)进行了Relax,那么有 delta(s,$v_1$)=d[$v_1$]
进行第二次循环,取到的边($v_1$,$v_2$)进行了Relax,那么有delta(s,$v_2$)=d[$v_2$]
那么通过k轮循环以后,有delta(s,$v_k$)=d[$v_k$],也就是说通过了|V|-1轮循环以后,每一个从源点可达的顶点都计算了最短路径

简单路径(simple path):指除了起点和终点以外,其它顶点不会重复。对于简单路径p=<$v_0$,$v_1$,...,$v_k$>来说,若是k>=|V|,那么路径上总的顶点数是|V|+1,但实际只有 |V|个顶点,那么一定存在一条重复的边,使得非起点终点重复了,也就是说他不是简单路径了

为何Bellman-Ford算法能检测负权重环?

通过|V|-1轮循环以后,若是还有一条边可以Relax,那么当前从s到v的最短路径并非简单路径,由于全部的节点都已经看过了,这时候确定存在了重复的节点,也就是说存在一个负权重的环

若是对一个路径上有环,且全部权重值都是负权重,那么使用Bellman-Ford算法能获得最长路径吗?

不能,由于Bellman-Ford对于存在负权重的环的时候只会抛出异常,并无计算路径,这实际是一个N-P的问题,即花的时间在指数级别或者之上

相似的,若是要求不通过负权重的环的状况下,计算最短路径,也并非件容易的事情
相关文章
相关标签/搜索