Dijkstra算法
基本算法:
将图G中所有的顶点V分成两个顶点集合A,B.如果源点S到顶点u的最短路径已经知道,则点u属于集合A,否则属于集合B。在最开始的时候A中只包含源点S,其余的点属于B,算法结束时,所有源点S可以达到的点都在集合A中,而不可达的点仍然在集合B。
伪代码:
S为源点,Belong[ ]记录点属于哪个集合,true表示属于A,false表示属于B,map[ ][ ] 记录地图信息,map[u][v]代表点v到点u的长度,最短结果保存到dist[ ]中,pre[ ]记录最短路径终点的前驱,如:到达点j最短的路径上,终点j的前一节点即为k,即pre[j] = k。
- 初始化:源点的距离dist[s]设为0,其他的点的距离设为map[s][i],p[s] = true,其他各点的 p[i] = false;
- 循环N-1次:
- 在B中的点取一s到其距离最小的点k,p[k] = true; 如果所有的k都不可达,退出循环,算法结束。
- 对于每个与k相邻的在B中的点j,更新s到j的最短路径,如果dist[k] + map[k][j] < dist[j] ;
那么dist[j] = dist[k] + map[k][j];
直接实现:
最简单的实现方法是,存储图的信息使用邻接矩阵,在每次循环中,在用一个循环找距离最短的点,然后用遍历的方法更新与其相邻的边,注意比较可以发现,除了路径记录和更新dist数组部分以外,Dijkstra和Prim算法实现完全相同。
样例代码:
const int maxn = 10001; //点个数
void Dijkstra(int map[maxn][maxn],int s)
// n个点,dist[i]表示点i到源点s的最短距离,map记录图信息,pre记录前驱,源点,终点。
{
int i, j, k;
int min;
bool belone[maxn]; //true 属于A,false属于B
int dist[maxn];
int pre[maxn];
for (i = 1; i < maxn; ++i) //初始化
{
belone[i] = false;
if (i != s)
{
dist[i] = map[s][i];
pre[i] = s;
}
}
dist[s] = 0;
belone[s] = true;
for (i = 1; i < maxn - 1; ++i)//循环n-1次,求s到其他n-1个点的最短路径
{
min = INT_MAX;
k = 0;
for (j = 1; j < maxn; ++j)//在b中取一点,s到其距离最短
{
if (!belone[j] && dist[j] < min)
{
min = dist[j];
k = j;
}
}
if (k == 0) return; //如果没有点可扩展,即剩余的点不可到达返回
belone[k] = true; //将k从B中除去,加入到A
for (j = 1; j < maxn; ++j)
{
//对于每个与k相邻的在B中的点j,更新s到j的最短路径
if (!belone[j] && map[k][j] != INT_MAX&&dist[j] > dist[k] + map[k][j])
{
dist[j] = dist[k] + map[k][j];
pre[j] = k;
}
}
}
}
效率:时间复杂度为O(n^2),空间复杂度:由于使用邻接矩阵,空间消耗为O(n^2)。
输出结果: