Dijkstra(迪杰斯特拉)算法: 即给定图和起点,通过算法得到起点到其余点的最短路径。主要步骤就是:每次从剩余顶点中选一个离起点最近的点,然后更新这个点周围的点离起点的距离,同时标记这个点。直到所有的点都被标记。
-
原理———之前看的一篇博客中有句解释这个过程的话感觉很棒:因为目前离 v1顶点最近的是 v3顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 v1顶点到 v3顶点的路程进一步缩短了。因为 v1顶点到其它顶点的路程肯定没有 v1到 v3顶点短,
如图:
按照算法的思路途中4个结点的遍历顺序是:v0、v1、v3、v2。第一次遍历过后我们选择了距离起点12的点v1,容易想到,我们不可能通过其他中转点,使得v0到v1的距离更短,但是我们可能通过v1这个中转点使得v0到v2或v3的距离更短。 -
如何记录路径——在算法的实现过程中,可以想到,我们得到的是一棵最短路径树,根据树的特点,每个结点都只有一个父亲结点,所以我们就可以用一个一维数组存储路径,最后从终点开始逐步向上层遍历到根节点(即起点),从而得到路径。如上图,第一次遍历过后v1、v2、v3的父亲结点都是v0;第二次遍历v1点时,发现v0通过v1到v2(v0–>v1–>v2)比v0直接到v2(v0–v2)更近,便更新v2的父亲结点为v1。
第一次遍历结束path数组:
0 | 1 | 2 | 3 |
---|---|---|---|
-1 | 0 | 0 | 0 |
第二次遍历结束path数组:
0 | 1 | 2 | 3 |
---|---|---|---|
-1 | 0 | 1 | 0 |
- 代码如下:
#include <iostream>
#include <string.h>
#include <stack>
#include<stdio.h>
using namespace std;
#define MAX 100
#define INF 0x3f3f3f3f
#define me(a,b) memset(a,b,sizeof(b))
int dist[MAX],path[MAX];//储存最短距离和路径
struct MGraph
{
int edges[MAX][MAX];//邻接矩阵,记录的是两点之间的距离,也就是权值
int e,n;//边数和顶点数
} G;
void init()
{
memset(G.edges,INF,sizeof(G.edges));
//me(G.edges,(INF));
for(int i=0; i<G.n; i++)
G.edges[i][i]=0;
}
void printf_MG()
{
for(int i=0; i<G.n; i++)
{
for(int j=0; j<G.n; j++)
{
if(G.edges[i][j]==INF)
printf("∞ ");
else
printf("%2d ",G.edges[i][j]);
}
printf("\n");
}
}
void Dijkstra(MGraph g,int u)
{
int U[MAX],mmin;//分别表示已经遍历过的点、距当前起始点最近的点的距离
//对各数组进行初始化
memset(U,0,sizeof(U));
memset(path,-1,sizeof(path));
//me(dist,INF);
for(int i=0; i<g.n; i++)
{
dist[i]=g.edges[u][i];
if(g.edges[u][i]<INF)
path[i] =u;
}
dist[u]=0;//到本身的距离
for(int i=0; i<g.n; i++) //求出源点到n个点的最短距离
{
mmin=INF;
U[u]=1;//将选出的新的起始点放入U数组中
for(int j=0; j<g.n; j++)
//这个if判断顶点u的加入是否会出现通往顶点j的更短的路径,如果出现,则改变原来路径及其长度,否则什么都不做
{
if(!U[j]&&dist[u]+g.edges[u][j]<dist[j])
{
dist[j]=dist[u]+g.edges[u][j];//更新路径长度
path[j]=u;//更新到顶点j的路径
}
}
for(int j = 0; j < g.n; j++)
//这个循环每次从剩余顶点中选出一个顶点,通往这个顶点的路径在通往所有剩余顶点的路径中是长度最短的
{
if(U[j] == 0 && dist[j] < mmin)
{
u = j;
mmin = dist[j];
}
}
}
}
void printf_path(int u,int x)
{
int a[MAX],cou=0,ex=x;
if(u==x)
printf("%d-->%d=0",u,x);
else if(path[x]==-1)
printf("%d-->%d=∞",u,x);//没有路径
else
{
while(x!=u)
{
a[cou++]=x;
x=path[x];
}
a[cou]=x;
for(int i=cou; i>0; i--)
printf("%d-->",a[i]);
printf("%d=%d",a[0],dist[ex]);
}
printf("\n");
}
int main()
{
int v1,v2,w;
scanf("%d%d",&G.e,&G.n);//输入边数和顶点数
init();
for(int i=0; i<G.e; i++)
{
scanf("%d%d%d",&v1,&v2,&w);
G.edges[v1][v2]=w;
G.edges[v2][v1]=w;
}
printf_MG();//输出邻接矩阵
int u;
scanf("%d",&u);//输入源点
Dijkstra(G,u);
for(int i=0; i<G.n; i++)//输出源点到每个点的最短路径以及路径长路
printf_path(u,i);
return 0;
}
/*
13 10
0 1 10
0 2 15
1 3 20
1 4 5
2 5 8
2 6 6
3 7 7
4 5 10
4 7 3
4 8 4
5 6 9
5 8 3
6 8 12
*/
样例对应的无向图(因为两个结点之间可以互通,所以存储的时候需要双向存储)
样例输出:
- 如果只需要得到起点到一个顶点的最短路径,把子函数void Dijkstra(MGraph g,int u)中最外层循环for(int i=0; i<g.n; i++)改为条件while(!U[i]),即当终点是剩余点中距离起点最近的点时,就可结束循环。