单源最短路径 1. 狄克斯特拉(Dijkstra)算法
狄克斯特拉(Dijkstra)算法
单源最短路径问题
给定一张有向图\(G=(V,E)\),\(V\)是点集,\(E\)是边集,\(|V|=n\),\(|E|=m\),节点以\([1,n]\)之间的连续整数编号,\((x,y,z)\)描述一条从\(x\)出发,到达\(y\),权值为\(z\)的有向边,求长度为\(n\)的数组\(dist\),其中\(dist[i]\)表示从起点\(s\)到节点\(i\)的最短路径的长度。
——选自李煜东《算法竞赛进阶指南》
理论知识部分
Dijkstra算法基于贪心思想:在寻找最短路时,先找每两个节点间的最短路,再依次扩大范围,直到求出\(dist\)数组。
步骤
1.初始化\(dist\)数组(0x3f3f3f3f),\(d[s]=0\),\(v\)数组初始化为\(false\)
2.找到\(dist[i]\)值最小且未被访问过的节点\(i\),标记已访问
3.扫描节点\(i\)的每一条出边(终点用\(to\)表示,权值用\(w\)表示),如果\(d[to] > d[i] + w\),则\(d[to]=d[i]+w\)
4.重复\(2\)~\(3\)步,直到所有节点都被访问过
代码实现部分
图的存储
参见这篇博客
算法主体
步骤\(2\)中\(dist\)值最小节点\(i\)可以用STL priority_queue实现,因为它是大根堆,又要表现\(dist\)值最小和节点编号\(i\),两个信息,所以可以用它存储一个二元组,第一元为\(-dist[i]\)(实现小根堆),第二元为节点编号\(i\)
priority_queue < pair<int,int> > q;
void dis(void)
{
memset(d,0x3f,sizeof(d));
d[s] = 0;
q.push( make_pair(0,s) );
while(q.size())
{
int x = q.top().second;//获取节点编号
q.pop();
if(vis[x])//是否被访问
continue;
vis[x] = 1;//标记已访问
for(int i = head[x],y ;i ;i = Next[i])//步骤3
{
y = edge[i];
if(d[y] > d[x] + weight[i])
{
d[y] = d[x] + weight[i];
q.push( make_pair(-d[y],y) );//下一个节点
}
}
}
for(int i = 1 ;i <= n ;i++)
cout << d[i] << ' ';//输出结果
}
总结
虽然本篇博客比较短,但该算法在图论中有重要地位,它也有着众多优化和变形。
请读者思考本算法的各种实现细节。