本文共 3609 字,大约阅读时间需要 12 分钟。
徐不可说 2018.7.21
摘要:迪杰斯特拉算法(Dijkstra算法)是由荷兰计算机科学家于1959 年提出的,因此又叫。是从一个顶点到其余各顶点的算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。该为n^2,且可以发现,如果边数远小于n^2,对此可以考虑用这种进行优化,取出最短路径的复杂度降为O(1);每次调整的复杂度降为O(elogn);e为该点的边数,所以复杂度降为O((m+n)logn)。
关键词:Dijkstra算法;最短路径算法;堆;时间复杂度;
迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块, 用于计算一个节点到其他节点的最短路径,它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。
Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。
首先,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点,然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。最后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。
大学经典教材<<数据结构>>(C语言版 严蔚敏 吴为民 编著) 中该算法的实现#include <iostream>
#include <cstdio>
#define MAX 1000000
using namespace std;
int arcs[10][10];//邻接矩阵
int D[10];//保存最短路径长度
int p[10][10];//路径
int final[10];//若final[i] = 1则说明 顶点vi已在集合S中
int n = 0;//顶点个数
int v0 = 0;//源点
int v,w;
void ShortestPath_DIJ()
{
for (v = 0; v < n; v++) //循环 初始化
{
final[v] = 0; D[v] = arcs[v0][v];
for (w = 0; w < n; w++) p[v][w] = 0;//设空路径
if (D[v] < MAX) {p[v][v0] = 1; p[v][v] = 1;}
}
D[v0] = 0; final[v0]=0; //初始化 v0顶点属于集合S
//开始主循环 每次求得v0到某个顶点v的最短路径 并加v到集合S中
for (int i = 1; i < n; i++)
{
int min = MAX;
for (w = 0; w < n; w++)
{
//我认为的核心过程--选点
if (!final[w]) //如果w顶点在V-S中
{
//这个过程最终选出的点 应该是选出当前V-S中与S有关联边
//且权值最小的顶点 书上描述为 当前离V0最近的点
if (D[w] < min) {v = w; min = D[w];}
}
}
final[v] = 1; //选出该点后加入到合集S中
for (w = 0; w < n; w++)//更新当前最短路径和距离
{
/*在此循环中 v为当前刚选入集合S中的点
则以点V为中间点 考察 d0v+dvw 是否小于 D[w] 如果小于 则更新
比如加进点 3 则若要考察 D[5] 是否要更新 就 判断 d(v0-v3) + d(v3-v5) 的和是否小于D[5]
*/
if (!final[w] && (min+arcs[v][w]<D[w]))
{
D[w] = min + arcs[v][w];
// p[w] = p[v];
p[w][w] = 1; //p[w] = p[v] + [w]
}
}
}
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> arcs[i][j];
}
}
ShortestPath_DIJ();
for (int i = 0; i < n; i++) printf("D[%d] = %d\n",i,D[i]);
return 0;
}
如果边数远小于n^2,对此可以考虑用这种进行优化,取出最短路径的复杂度降为O(1);每次调整的复杂度降为O(elogn);e为该点的边数,所以复杂度降为O((m+n)logn)。实现过程如下:将与源点相连的点加入,并调整堆。选出堆顶元素u(即代价最小的元素),从堆中删除,并对堆进行调整。 处理与u相邻的,未被访问过的,满足三角不等式的顶点。若该点在堆里,更新距离,并调整该元素在堆中的位置。若该点不在堆里,加入堆,更新堆。 若取到的u为终点,结束算法;否则重复步骤2、3。
代码:
procedureDijkstra;
var
u,v,e,i:longint;
begin
fillchar(dis,sizeof(dis),$7e);//距离
fillchar(Inh,sizeof(Inh),false);//是否在堆中
fillchar(visit,sizeof(visit),false);//是否访问过
size:=0;
e:=last[s];
whilee<>0do//步骤1
begin
u:=other[e];
ifnot(Inh[u])then//不在堆里
begin
inc(size);
heap[size]:=u;
dis[u]:=cost[e];
Loc[u]:=size;//Loc数组记录元素在堆中的位置
Inh[u]:=true;
Shift_up(Loc[u]);//上浮
end
else
ifcost[e]<dis[u]then//在堆里
begin
dis[u]:=cost[e];
Shift_up(Loc[u]);
Shift_down(Loc[u]);
end;
e:=pre[e];
end;
visit[s]:=true;
whiletruedo
begin
u:=heap[1];//步骤2
ifu=tthenbreak;//步骤4
visit[u]:=true;
heap[1]:=heap[size];
dec(size);
Shift_down(1);
e:=last[u];
whilee<>0do//步骤3
begin
v:=other[e];
ifNot(visit[v])and(dis[u]+cost[e]<dis[v])then//与u相邻的,未被访问过的,满足三角不等式的顶点
ifInh[v]then//在堆中
begin
dis[v]:=dis[u]+cost[e];
Shift_up(Loc[v]);
Shift_Down(Loc[v]);
end
else//不再堆中
begin
inc(size);
heap[size]:=v;
dis[v]:=dis[u]+cost[e];
Loc[v]:=size;
Inh[v]:=true;
Shift_up(Loc[v]);
end;
e:=pre[e];
end;
end;
writeln(dis[t]);
end;
求最短路径问题中不仅仅用迪杰斯特拉算法(Dijkstra算法)可以解决,、prim算法、等都可以实现。荷兰计算机科学家狄克斯特拉于1959年提出迪杰斯特拉算法十分经典,优点在于理解方便,适合算法初学者优先学习,缺点是效率较低,但是我们建立在堆的基础上仍能对该算法进行一定优化以降低时间复杂度。
参考文献
[1] .nocow[引用日期2018-07-19]
[2] .博客园[引用日期2018-07-19]
[3] 最短路径算法.《信息学奥赛》.科学技术文献出版社[引用日期2018-07-19]
[4] 最短路问题.《挑战程序设计竞赛》.人民邮电出版社 [引用日期2018-07-19]
转载地址:http://zatgn.baihongyu.com/