单源最短路径-Dijkstra算法

1.算法标签

贪心

2.算法描述

具体的算法描述网上有好多,我觉得莫过于直接wiki,只说明一些我之前比较迷惑的。

对于Dijkstra算法,最重要的是维护以下几个数据结构:

  • 顶点集合S : 表示已经找出从源点出发最短路径的顶点集合
  • 顶点集合Q: S在所有顶点集合中的补集,即V-S
  • 距离数组dist : 在程序执行过程中,如果序号为n的顶点已经在S中,那么dist[n]表示从源点start到顶点n的最短距离,否则dist[n]的值将在程序执行过程中不断收敛。
  • 路径数组previous: 当程序执行完成之后,previous[n]表示最短路径中顶点n的前一个顶点

程序执行时,每轮循环中,对Q中每个顶点u,计算源点经由S中顶点直接到达u的路径长度最小值,存入dist数组中,每轮循环只往S中加入一个顶点,这个顶点是Q中顶点dist值最小的那个,因此这是个贪心策略,为什么贪心策略是正确的?

  • Dijskstra默认每条边都是正权值
  • 因为S中的顶点的最短路径已经找到,所以加入的这个点肯定找不到一条更短的到源点的路径,否则说明S中的路径不是最短路径。

3.实现说明

实现时,Dijkstra算法可能会有多种改进,比如图用邻接表的形式存储,可以在稀疏图中获得更快的时间,然后计算dist最小值可以用最小堆来实现,这样就不必每次线性查找

4.一个例子(图片来源

image

5.代码实现

//aouthor:areslipan
vector<int> Dijkstra(vector<vector<int> > &g, int vNum,int start)
{
vector<int>path(vNum,-1);//-1表示路径未找到
vector<int>dist(vNum,MYINF);
dist[start]=0;
set<int>source;//一旦一个点距离源点start最短路径被找到,那么该点就被加入集合source中
set<int>dest; //source 的补集
for(int i=0;i<vNum;++i)dest.insert(i);


set<int>::iterator iter_s;
set<int>::iterator iter_d;

int cur=start;

while(!dest.empty())
{
int curMin=MYINF;

//选出dest中dist最小的那个归入source中
for(iter_d=dest.begin();iter_d!=dest.end();++iter_d)
{
if(curMin>dist[*iter_d])
{
curMin=dist[*iter_d];cur=*iter_d;
}
}

source.insert(cur);
dest.erase(cur);

//加入一个新顶点之后开始更新dest中顶点的dist值,此处可以用最小堆优化
for(iter_s=source.begin();iter_s!=source.end();++iter_s)
{
for(iter_d=dest.begin();iter_d!=dest.end();++iter_d)
{
if((g[*iter_s][*iter_d]+dist[*iter_s])<dist[*iter_d])
{
dist[*iter_d]=g[*iter_s][*iter_d]+dist[*iter_s];
path[*iter_d]=*iter_s;
}
}
}
}

return path;
}

上面这个程序算法时间复杂度较高,在优化实现的时候算法复杂度可以降低到O(NlgN),分析见wiki,这个程序只是作为说明的用途。

6.完整的程序文件

图算法框架

7.参考

参考1:http://www.wutianqi.com/?p=1890

参考2: wiki

参考3:文中链接

posted @ 2013-09-30 12:53  曾见绝美的阳光  阅读(475)  评论(0编辑  收藏  举报