迪杰斯特拉(Dijkstra)算法
定义
Dijkstra(迪杰斯特拉)算法是计算单源最短路径算法,用于计算一个结点到其他所有结点的最短路径。该算法以源点为起始点,不断更新其他点到已经确定距离结点的距离,选取距离最小的结点加入S集合,直到S集合存放有所有的结点
算法思想
现在一张图中有n个结点,有两个集合,S集合和V集合。S集合表示已经选取的结点,V集合表示还没有选取的结点
- 确定一个源点,放入S集合,剩下n-1个结点放入V集合
- 计算从源点经过S集合中的各点到达V集合中各点的距离,与之前确定的距离进行比较,如果新计算的距离小于原先得距离则更新该结点的距离,否则不更新距离,并选取距离最小的结点放入S集合,直到V集合为空(在计算过程中直接计算从最后加入S集合的点到源点的距离+最后加入S集合的点到V集合中各点的距离,表示从源点到达V集合中各点要经过最后加入S集合中的点,与原来的距离比较即可)
还是很抽象,举个例子
示例
我们以有向图为例
比如我们选取结点1为源点,利用Dijkstra算法计算源点到其他各点的距离
那么现在S=[1],V=[2,3,4,5]
计算此时从源点出发经过最后加入S集合中的点(源点)到达V集合中各点的距离
经过S集合中的各点到达2的距离:10
经过S集合中的各点到达3的距离:infinity
经过S集合中的各点到达4的距离:30
经过S集合中的各点到达5的距离:100
此时选取距离最小的结点2加入S集合,即S=[1,2],V=[3,4,5]
计算此时从源点出发经过最后加入S集合中的点(结点2)到达V集合中各点的距离
如果新计算的距离小于原先得距离则更新该结点的距离,否则不更新距离
源点到结点2的距离+结点2到V集合中结点3的距离:10+50=60,小于infinity,更新
源点到结点2的距离+结点2到V集合中结点4的距离:10+infinity,不小于原来的30,不更新
源点到结点2的距离+结点2到V集合中结点5的距离:10+infinity,不小于原来的100,不更新
故:
3:60
4:30
5:100
此时选取距离最小的结点4加入S集合,即S=[1,2,4],V=[3,5]
计算此时从源点出发经过最后加入S集合中的点(结点4)到达V集合中各点的距离,如果新计算的距离小于原先得距离则更新该结点的距离,否则不更新距离
源点到结点4的距离+结点4到V集合中结点3的距离:30+20=50,小于原来的60,更新
源点到结点4的距离+结点4到V集合中结点5的距离:30+60=90,小于原来的100,更新
故:
3:50
5:90
此时选取距离最小的结点3加入S集合,即S=[1,2,4,3],V=[5]
计算此时从源点出发经过最后加入S集合中的点(结点3)到达V集合中各点的距离
源点到结点3的距离+结点3到V集合中结点5的距离:50+10=60,小于原来的90,更新
故:
5:60
此时选取距离最小的结点5加入S集合,即S=[1,2,4,3,5],V=[ ]
因为在生成最短路径的过程中,从源点到达V集合中各点时都要经过S集合中的各点,故生成的最短路径如下:
在加入顶点的过程中我们得到了单源最短路径,从源点到2,3,4,5的距离分别为:10,50,30,60
代码
#include<iostream>
using namespace std;
const int inf = 999;
void Dijkstra(int n,int source,int dist[],int prev[],int c[6][6])
{
bool s[inf];//用于表示点是否在s集合里
for(int i=1; i<=n; i++)
{
dist[i] = c[source][i];
s[i] = false;//初始化的时候,除了source外所有的点都不在 s集合
if(dist[i] == inf) prev[i] = 0;
else prev[i] = source;//如果相邻,前驱点是源点
}
dist[source] = 0;
s[source] = true;
for(int i=2; i<=n; i++)//没有用到i的值,只是控制循环次数,表示要找n-1个点
{
int minDist = inf;
int u = source;//u记录下距离source最近的点
for(int j=1; j<=n; j++) //从v-s集合中找点
{
//j点不在s集合中,且到source的距离小于上一个点到source的距离,就用u记录下该点
if(!s[j] && dist[j]<minDist)
{
u = j;
minDist = dist[u];
}
}
s[u] = true;//将u点加入s集合
for(int j=1; j<=n; j++)//当s集合加入新点的时候需要更新dist[]和prev[]
{
if(!s[j] && c[u][j]<inf)//j点不在s集合中,且 新点与 j点相邻
{
int newDist = dist[u] + c[u][j];//新点到source的距离 + 新点到 j点的距离
if( newDist < dist[j] )
{
dist[j] = newDist;
prev[j] = u;//新点成了j的前驱点,表示此时从source到j点经过u距离最短
}
}
}
}
}
int main()
{
/*
n个点
dist[i]表示从source点到 i点的最短距离
k=previous[i]表示经过 k点到达 i点才是最短路径
c[][]是临接矩阵
*/
int c[6][6]={
{inf,inf,inf,inf,inf,inf},
{inf,0,10,inf,30,100},
{inf,inf,0,50,inf,inf},
{inf,inf,inf,0,inf,10},
{inf,inf,inf,20,0,60},
{inf,inf,inf,inf,inf,0}
};
int dist[6];
int prev[6];
int source = 1;
int n = 5;
Dijkstra(n,source,dist,prev,c);
cout<<"从源点到各点的最短路径是(距离为0表示源点):";
for(int i=1; i<=n; i++)
cout<<dist[i]<<" ";
cout<<endl;
cout<<"下面输出从源点到各点的反向路径"<<endl;
for(int i=1; i<=n; i++)
{
if(i != source)
{
int j = i;
cout<<"从源点到"<<j<<"点的最短路径为:"<<endl;
int k = j;
while(k != source)
{
if( j!=source )
cout<<j<<" <- ";
else
cout<<j;
k = j;
j = prev[j];
}
cout<<endl;
}
}
return 0;
}