Dijkstra算法
Dijkstra算法解决的是带权重的有向图上单源最短路径问题,所谓单源最短路径,就是固定一个顶点为源点,求源点到其他每个顶点的最短路径,该算法要求图上所有边的权值为非负值。
Dijkstra算法在运行过程中,将整个图划分为两个点集合S与T,其中源点s到点集合S中所有顶点的最短路径已经被找到,而s到T中所有顶点的最短路径还没有被找到。初始时,集合S中只有源点s;算法重复地从点集合T中选择当前长度最小的一条最短路径的顶点u,将u加入到集合S,然后修改从顶点0到T中各顶点的最短路径长度;重复这一步骤,直到所有顶点都加入到集合S中,算法结束。在下面给出的伪代码中,我们利用一个最小优先队列实现该算法,优先队列中保存的元素为(源点到顶点u的最短距离d[u],顶点u),优先队列中的元素按照关键字d[u]升序排列。
初始化,清除所有点的标号
令d[0] = 0,其他d[i] = INF
源点s入队列
队列非空{
在所有未标号的顶点中选择d值最小的顶点u
u出队
给顶点u标号
对于所有从u出发的边(u,v),更新d[v] = min{d[v],d[u] + w(u,v)}
}
Dijkstra算法的执行过程如下所示:
1.源点0入队
2.源点0出队,更新d[1] = 1、d[2] = 7,顶点1、2入队
3.顶点1出队,更新d[3] = 10、d[5] = 16,顶点3、5入队
4.顶点2出队,更新d[4] = 11,顶点4入队
5.顶点3出队,更新d[5] = 15
6.顶点4出队,更新d[5] = 14
7.顶点5出队,算法结束
使用邻接矩阵实现的Dijkstra算法的时间复杂度为O(|V|^2),而使用邻接表的话,更新最短距离只需要访问每条边一次即可,因此这部分的时间复杂度为O(|E|)。但是每次要枚举所有顶点来查找下一个使用的顶点,因此最终复杂度还是O(|V|^2)。使用堆优化后,每次从堆中取出的最小值都是下一次要使用的顶点。这样堆中的元素共有O(|V|)个,更新和取出数值的操作有O(|E|)次,因此整个算法的复杂度是O(|E|log|V|)。
下面给出使用C++实现的Dijkstra算法核心代码
typedef pair<int,int> P;//first是最短距离,second是顶点的编号
struct edge
{
int to;
int dis;//权值
edge(int to,int dis)
{
this -> to = to;
this -> dis = dis;
}
};
vector<edge> G[maxn];
int d[maxn];//最短距离
int V,E;//V顶点数,E边数
//初始化
void init()
{
for(int i = 0;i < V;i++)
G[i].clear();
}
//建图
void add_edge(int from,int to,int dis)
{
G[from].push_back(edge(to,dis));
}
void dijkstra(int s)
{
//通过指定greater<P>参数,堆按照first从小到大的顺序取出值
priority_queue<P,vector<P>,greater<P> > que;
for(int i = 0;i < V;i++)
d[i] = INF;
d[s] = 0;
que.push(P(0,s));
while(que.size()){
P p = que.top();
que.pop();
int v = p.second;
if(d[v] < p.first)
continue;
for(int i = 0;i < (int)G[v].size();i++){
edge e = G[v][i];
if(d[e.to] > d[v] + e.dis){
d[e.to] = d[v] + e.dis;
que.push(P(d[e.to],e.to));
}
}
}
}
PS:水平有限,如有错误,请路过的各位指正,谢谢!