半路算法之Dijkstra——以路由算法的角度进行理解

半路算法之Dijkstra——以路由算法的角度进行理解

前一阵无线传感网络课程(wsn)布置了一项任意语言任意环境实现dijkstra算法的作业。我一琢磨,好啊,之前一直喊着没时间安心搞算法,这次可以作为切入点啊!
不过,真正开始写之后才发现问题重重,自己要提升的地方还有很多。

捧出神器《算法导论》,翻到第六部分图算法中的Dijkstra算法开始研究:

Dijkstra算法解决了有向图G=(V, E)上带权的单源最短路径,且权值不为负。
即是说Dijkstra的适应问题有以下特征:单源、非负权、有向(虽然到现在我也不懂为什么老师给出的图是无向图,按照我的理解是它对无向图同样适用)。
且在尽可能的优化下,Dijkstra算法比Bellman-Ford算法效率高。

先列出算法的伪代码:

initialize-single-source(G, s)
S <- NULL
Q <- V[G]
while Q != NULL
    do 
        u <- extract-min(Q)
        S <- S+{u}
        for each vertex v in Adj[u]
            realx(u, v, w)

这一部分我没有完全照抄书上内容,有异议的地方还是按照《算法导论》书中内容来吧。
简单分析一下:集合S存放已经按照最短路径与源节点连起来的,对应到无线传感网络中即是存放的路由表信息;Q是最小优先队列(书中提到以斐波那契堆实现可以进一步提高效率,近期将对之进行实现),我在具体实现时以二维数组进行反复遍历,显然效率是不如斐波那契堆的;第六、七行从Q中取得距离最小点并将该点加入集合S中,需要注意的是这个距离最小点指的是该点是目前距离源节点距离最短的点;最后两行是对以u为起点的每条边(u, v)进行松弛。
总体来说,整个流程是把最小优先队列Q中的点按照最小优先的思路逐个挪到集合S中。整个大循环while迭代次数为|V|次(即总的节点数)。


虽然Dijkstra算法使用了贪心策略,但是它仍旧取得了全局优先。它是如何做到的呢?

即证:每当一个顶点u插入到集合S中时,d[u]=δ[s, u]。(δ是s到u的最短路径)

我们用反证法进行证明。
设x是加入到集合S中的第一个d[x]≠δ(s, x)的点。
首先s源节点作为第一个加入到集合S中的节点,因为x≠s,因此在x加入集合S时,集合S非空。
那么从s到x一定存在一条路径,否则依据无路径性质d[x]=δ[s, x]=∞,与假设矛盾。
此时我们假设节点a是s->x路径上第一个不在集合S中的点,而节点b是节点a的前一个节点。由于我们的假设是点x是第一个不是距离最短的点(好绕口),那么至少从s到b都是距离最短的。
由于边的权值非负,而a肯定在x出现之前,因此根据上界性质我们有:
d[a]=δ[s, a]≤δ[s, x]≤d[x]
注意看我们的伪代码中倒数第四行,是从最小优先队列中取得的节点,即x是当前Q中距离集合S最短的一个节点,而点x和点a同在Q中。那么也就是说点x和点a为同一点,即d[x]=δ(s, x),与假设矛盾。所以每当一个顶点u插入到集合S中时,d[u]=δ[s, u]。我们得到的集合S是全局优先的。
(我觉得《算法导论》书中用u来表示假设不满足的节点有点乱,就换了下。具体的对应关系是u->x; y->a; x->b,有书的同学可以对应起来看,没有的略过括号内容好了。)

书中还有一个推论,是说:权值非负源点为s的带权有向图G=(V, E)经过Dijkstra算法后前趋子图是以s为根的最短路径树。嗯,很好理解的(这不废话吗)。


最后依据书上内容简单再分析一下该算法的优化方法。由于书上说的挺详细的,所以这里就只说结论,不展开了。
我是以邻接矩阵的形式写成的代码,总计运行时间是O(V²+E)=O(V²)。显然这适合点少边多的情形。
如果是稀疏图,即E=o(V²/lgV)。此时用二叉最小堆较为适宜(它比斐波那契堆简单不止一倍吧)。总计运行时间O(V+(E) lgV),如果所有顶点有连接到源点的边,则为O(E lgV)。
而用斐波那契堆,则可将其提升到O(V lgV+E)。考虑到V个extract-min的平摊时间为O(lgV),而E个decrease-key的平摊时间为O(1),且decrease-key的操作比extract-min的操作多得多。斐波那契堆的extract-min的平摊时间比二叉最小堆少,这就使得斐波那契堆更快。extract-key次数越多越明显。


以C语言实现的最小邻接矩阵Dijkstra算法见我的github。
算法效率:linux(AMD A4-4300M APU, g++ 5.4.0)下300us, windows7(i7-4710MQ, g++ 5.3.0)下3000us。
https://github.com/ds1231h/myDijkstra

星期二, 11. 四月 2017 10:22下午

你可能感兴趣的:(C/C++)