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.一个例子(图片来源)
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:文中链接