您的位置:首页 > 编程语言 > Go语言

Dijkstra's Algorithm分析以及其优化

2017-07-25 21:57 127 查看
Dijkstra's  Algorithm



传统的Dijkstra:
适用于单源无负权边最短路问题。
算法步骤:
a.初始时,S只包含源点,即S={v},v的距离为0。

U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则<u,v>正常有权值,
若u不是v的出边邻接点,则<u,v>权值为∞。
b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
c.以k为新考虑的中间点,修改U中各顶点的距离;

若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,
则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
d.重复步骤b和c直到所有顶点都包含在S中。





//核心代码:
void  Dijkstra(int  s,int t)//s为起点
{//t为终点
int temp, k;
ans=-1;
for (int i=0; i<n; i++)//对起点邻点操作
{
dis[i]=map[s][i];
vis[i]=0;
}
dis[s]=0;
vis[s]=1;//说明s点已被标记
for (int i=0; i<n; i++)
{
temp = inf;//inf为超大的数
for (int j=0; j<n; j++)
{
if(vis[j]==0 && temp > dis[j])
{//j没有被标记且是已知点最小的
k=j;
temp=dis[j];
}
}
if(temp == inf) //说明全被标记
break;
vis[k]=1;
for (int j=0; j<n; j++)
{//看通过k点是否会取得更小值
if(dis[j] > dis[k] + map[k][j])
dis[j]=dis[k]+map[k][j];
}
}
if(dis[t]!=inf)
ans = dis[t];
}


传统dijkstra的不足之处:
(1)用邻接矩阵arcs来存储网络图,其存储量为

NxN。对于大型稀疏矩阵,这将耗费大量资源存储那些

无意义的矩阵元素。

(2)当从未标记节点集合T选定下一个节点vj作

中间节点后,在更新操作过程中,需要扫描所有的未标

记节点并进行比较更新。而未标记节点集合T中往往

包含大量与中间节点V;不直接相连的节点。

(3)在选择下一个最短路径节点作为中间节点时,

需要比较所有的未标记节点。而这个中间节点往往包含

在与已标记节点S集合的所有节点邻接的节点中。

(4)在算法的每次迭代中,由于未标记节点以无序

的形式存放在一个链表中或一个数组中.每次选择最短

路径节点都必须将所有未标记节点扫描一遍,当节点数

目很大时,这无疑将成为制约计算速度的关键冈素。

基于优先队列的优化

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
using namespace std;

#define INF  0x3f3f3f3f   //定义一个很大的数

struct Node
{
int num,val;   //存放结点编号和到初始点的距离
} nod;

priority_queue<Node> qq;;   //优先从小到大

bool operator < (Node a,Node b)
{
if(a.val == b.val) return a.num>b.num;
return a.val>b.val;              //先出小
}

int book[100];  //检查这个点是否用过
int dis[100];     //到原点最短距离
int D[100][100];  //记录路径长度
int V,E;

int main()
{
int a,b,d;
while(cin>>V>>E && V&& E)  //输入顶点数和边数
{
while(!qq.empty()) qq.pop(); //清空
memset(book,0,sizeof(book));
memset(D,-1,sizeof(D));

for(int i=0; i<E; i++)
{
cin>>a>>b>>d;
D[a][b] = D[b][a] = d;
}

for(int i=2; i<=V; i++)
dis[i]=INF;

dis[1]=0;
nod.num=1;
nod.val=0;

qq.push(nod);   //将起点放入队列

while(!qq.empty())  //不为空时
{

for(int i=2; i<=V; i++)
{
if(D[qq.top().num][i] != -1  &&dis[i]>dis[qq.top().num] + D[qq.top().num][i])
{

dis[i]=dis[qq.top().num] + D[qq.top().num][i];
nod.num=i;
nod.val=dis[i];
qq.push(nod);
}
}

qq.pop();
}

for(int i=1; i<=V; i++)
{
cout<<"初始点到"<<i<<"点的距离为:"<<dis[i]<<endl;
}

}
return 0;
}

完全优化后空间复杂度可以从O(N*N)到O(4*N+E),时间复杂度可以从O(N*N)到O(n*(logN+E),具体复杂度分析不在此解析,详情可见参考论文。

在此只是在时间复杂度上有一定的优化,空间复杂度的优化可以用邻接表进行对边的存储。堆优化以及空间优化将在后续更新。

本文参考:王战红1 孙明明2姚 瑶3《Dijkstra算法的分析与改进》的论文。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息