Dijkstra算法的原理:
从某个源点到其余各顶点的最短路径,即单源点最短路径(仅适合非负权值图)。单源点最短路径是指:给定带权有向图G和源点v,求从v到G中其余各顶点的最短路径。迪杰斯特拉(Dijkstra)提出了按路径长度递增的顺序产生各顶点的最短路径算法。
该算法的基本思想是:
(1)设置两个顶点的集合S和T=V-S,集合S中存放已找到最短路径的顶点,集合T存放当前还未找到最短路径的顶点;
(2)初始状态时,集合S中只包含源点v0;
(3)从集合T中选取到某个顶点vi(要求vi到v0的路径长度最小)加入到S中;
(4)S中每加入一个顶点vi,都要修改顶点v0到T中剩余顶点的最短路径长度值,它们的值为原来值与新值的较小者,新值是vi的最短路径长度加上vi到该顶点的路径长度;
(5)不断重复(3)和(4),直到S包含全部顶点。
算法设计:
1.首先函数里面运用二维数组cost[n][n]实现图的临界矩阵存储,数组dist[n]表示源点到节点n的最短距离,S[n]表示某一节点n是否已经进入集合S,如果进入则将S[i]置为1,否则为0。pre[n]表示当前节点n的前驱节点(用来输出路径)。
2.在开始遍历之前,首先给数组D[n]赋值为源点到该点的距离,这样便能第一次找到源点到相邻节点的最短距离(dist[i]=cost[v][i];)。
3.下面找出最短距离:
if((!S[j])&&(dist[j]<min))
{
min=dist[j];
u=j;
}
4.更新各节点的最短距离:
for(int k=0;k<n;k++)
{
if((!S[k])&&(dist[k]>dist[u]+cost[u][k])) //调整未加入S的点的距离值
{
D[k]=D[u]+cost[u][k];
pre[k]=u; //若通过u减小了k的距离值,则修改k的前趋为u
}
}
4.另外,若从原点无法到达顶点x,则令其前趋为-1:pre[x]=-1,在输出判别一下就可以了。还要提醒的一点是输入输出的顶点的标号与实际存储的数组下标相差为1,应该要分辨清楚。
代码实现:
#include<cstdio>
using namespace std;
#define max 10000
#define inf 20000
int n,e; //顶点数n,边的条数e
int cost[20][20]; //临界矩阵
int dist[20]; //存储最短路径的长度值
int pre[20]; //存储一个顶点在其最短路径的前趋
int S[20]; //标志数组,若为已经找到最短路径的结点则为1,否则为0
void Dijkstra(int v){
for(int i=0;i<n;i++){
dist[i]=cost[v][i]; //初始化
S[i]=0; //标志位初始为0
if(dist[i]<max)
pre[i]=v; //若存在边,则前趋为原点
else
pre[i]=-1; //否则,前趋为-1,不可达
}
S[0]=1; //原点标志为1
for(int i=0;i<n-1;i++){ //循环n-1次
int u; //u为待选顶点
int min=inf; //令初始最小值>max,使距离值为max的顶点也能加到S中
for(int j=0;j<n;j++){
if((!S[j])&&dist[j]<min){ //寻找距离S最小的顶点u
min=dist[j];
u=j;
}
}
S[u]=1; //将其标志设置为1
for(int k=0;k<n;k++){ //调整未加入S的点的的距离值
if((!S[k])&&dist[k]>dist[u]+cost[u][k]){
dist[k]=dist[u]+cost[u][k];
pre[k]=u; //若通过u减小了k的距离,则修改k的前趋为u
}
}
}
printf("\nThe result:\n"); //输出结果
for(int i=0;i<n;i++){
printf("<%d,%d>: ",v+1,i+1);
int p=pre[i];
if(p!=-1){ //若可达输出最短路径
printf("%d ",dist[i]); //输出最短距离
printf("%d",i+1); //根据前趋逆向输出最短路径
while(p!=v){
printf("<--%d",p+1);
p=pre[p];
}
printf("<--%d",v+1);
}
else{ //若不可达则输出“inf”
printf("inf");
}
printf("\n");
}
}
int main(){
printf("Please enter the number of n and e:");
scanf("%d%d",&n,&e);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cost[i][j]=max; //初始化为max
int start,end,dut;
for(int i=0;i<e;i++){
scanf("%d%d%d",&start,&end,&dut); //输入边的始点,终点和权值
start--;
end--; //结点号与存储的下标相差1
cost[start][end]=dut;
}
int v=0;
Dijkstra(v); //以顶点1(即下标为0)为原点v
return 0;
}