为防止广告,目前nocow只有登录用户能够创建新页面。如要创建页面请先登录/注册(新用户需要等待1个小时才能正常使用该功能)。
Dijkstra算法
这篇文章可以证实是由NOCOW用户原创,不存在任何版权争议。 本文作者同意以GNU FDL、CC-by-sa和GNU LGPL(如果适用)三种版权发布此文章(不包括翻译文章中属于原始所有者的部分版权)。 如果你修改了这篇文章并且不同意用GNU FDL以外的版权发布,可以换一个版权模板或者移除此模板。
Dijkstra算法是一种求单源最短路的算法,即从一个点开始到所有其他点的最短路。其基本原理是:每次新扩展一个距离最短的点,更新与其相邻的点的距离。当所有边权都为正时,由于不会存在一个距离更短的没扩展过的点,所以这个点的距离永远不会再被改变,因而保证了算法的正确性。不过根据这个原理,用Dijkstra求最短路的图不能有负权边,因为扩展到负权边的时候会产生更短的距离,有可能就破坏了已经更新的点距离不会改变的性质。
如果用本算法求一个图中全部的最短路,则要以每个点为源调用一次Dijkstra算法。
目录 |
[编辑] 适用条件与限制
[编辑] 算法流程
在以下说明中,s为源,w[u,v]为点u和v之间的边的长度,结果保存在dist[]
- 初始化:源的距离dist[s]设为0,其他的点距离设为无穷大,同时把所有的点的状态设为没有扩展过。
- 循环n-1次:
- 在没有扩展过的点中取一距离最小的点u,并将其状态设为已扩展。
- 对于每个与u相邻的点v,执行Relax(u,v),也就是说,如果dist[u]+w[u,v]<dist[v],那么把dist[v]更新成更短的距离dist[u]+w[u,v]。此时到点v的最短路径上,前一个节点即为u。
- 结束。此时对于任意的u,dist[u]就是s到u的距离。
[编辑] 算法实现
function [min,path]=dijkstra(w,start,terminal) n=size(w,1); label(start)=0; f(start)=start; for i=1:n if i~=start label(i)=inf; end end s(1)=start; u=start; while length(s)<n for i=1:n ins=0; for j=1:length(s) if i==s(j) ins=1; end end if ins==0 v=i; if label(v)>(label(u)+w(u,v)) label(v)=(label(u)+w(u,v)); f(v)=u; end end end v1=0; k=inf; for i=1:n ins=0; for j=1:length(s) if i==s(j) ins=1; end end if ins==0 v=i; if k>label(v) k=label(v); v1=v; end end end s(length(s)+1)=v1; u=v1; end min=label(terminal); path(1)=terminal; i=1; while path(i)~=start path(i+1)=f(path(i)); i=i+1 ; end path(i)=start; L=length(path); path=path(L:-1:1);
调用
edge= [ 2,3,1,3,3,5,4, 4,1,7,6,6,5, 5,11, 1,8,6,9,10,8,9, 9,10;... 3,4,2,7,5,3,5,11,7,6,7,5,6,11, 5, 8,1,9,5,11,9,8,10,9;... 3,5,8,5,6,6,1,12,7,9,9,2,2,10,10,8,8,3,7, 2, 9,9, 2, 2]; n=11; weight=inf*ones(n, n); for i=1:n weight(i, i)=0; end for i=1:size(edge,2) weight(edge(1, i), edge(2, i))=edge(3, i); end [dis, path]=dijkstra(weight, 1, 11)
[编辑] 优化
最简单的实现方法就是,在每次循环中,再用一个循环找距离最短的点,然后用任意的方法更新与其相邻的边,时间复杂度显然为
对于空间复杂度:如果只要求出距离,只要n的附加空间保存距离就可以了(距离小于当前距离的是已访问的节点,对于距离相等的情况可以比较编号或是特殊处理下)。如果要求出路径则需要另外V的空间保存前一个节点,总共需要2n的空间。
[编辑] 二叉堆实现
使用二叉堆(Binary Heap)来保存没有扩展过的点的距离并维护其最小值,并在访问每条边的时候更新,可以把时间复杂度变成。
用邻接表保存边,使得扩展边的总复杂度为O(E),否则复杂度不会减小。
空间复杂度:这种算法需要一个二叉堆,及其反向指针,另外还要保存距离,所以所用空间为3V。如果保存路径则为4V。
具体思路:先将所有的点插入堆,并将值赋为极大值(maxint/maxlongint),将原点赋值为0,通过松弛技术(relax)进行更新以及设定为扩展。
[编辑] 程序
[编辑] 扩展
[编辑] 第k短路
当k比较小时,可以直接在每个点保存k条最短路。更新的时候对每条能更新的路都更新一遍。此时每次更新的代价相当于把两个长度为k的表合并在一起,所以复杂度为纯Dijkstra实现的复杂度×O(k)。
曹氏短边法:每次将任意一条边赋值为MAX,重复计算数次后得到k短路径。
[编辑] 练习
图 - 有向图 - 无向图 - 连通图 - 强连通图 - 完全图 - 稀疏图 - 零图 - 树 - 网络
基本遍历算法:宽度优先搜索 - 深度优先搜索 - A* - 并查集求连通分支 - Flood Fill
最短路:Dijkstra - Bellman-Ford(SPFA) - Floyd-Warshall - Johnson算法
强连通分支:Kosaraju - Gabow - Tarjan
网络流:增广路法(Ford-Fulkerson,Edmonds-Karp,Dinic) - 预流推进 - Relabel-to-front
图匹配 - 二分图匹配:匈牙利算法 - Kuhn-Munkres - Edmonds' Blossom-Contraction