简介
在现实与竞赛中经常会遇到求最短路径的问题,比如求快递员送货的最短路径、两个村庄之间的来往的最短路径等等。一般的,给定n个点及每两个有联系的点之间的距离,在暴力求解的过程中,将每个两个点的所有路径遍历并记录再求自小距离,这样的运算相当耗时,可以得出,时间复杂度约为O(2^N)。很明显,我们需要对其进行优化。
下面介绍的Dijkstra算法和SPFA算法将可以很好的解决最短路径问题。
Dijkstra算法
这个算法的使用需要给定点按条件能连成不带负权值的图。此算法的思想是基于贪心法,首先,将每两个点之间的距离记录下来,两点之间无直接联系则用无穷大代替其距离,选择一个起点,每次循环开始从剩下的未选取过的点中选取一个与起点距离最近的点并以其为中间点,更新其他所有点到起点的距离,知道所有除起点外的点都被选取过循环结束。由于所有的边权都为正,因而不会出现一个距离更短且没被选取过的点,因而保证了算法的正确性。
算法设计:
1. 将图的所有顶点分为两组,一组为已经计算好距离的顶点,设为A组;另一组为还未计算的顶点,记为B组;初始时,所有顶点放在B组,接着将起点vi放入A组。
2. 从B组中找出离顶点vi最近的点vj(1<=j<=n),将vj加入A组,同时以vj为中间点,更新vi到B组中所有顶点的最短距离。
3. 重复第二个步骤直到所有的点从B组中加入到A组。
算法实现
用flag记录顶点在哪个组。当flag[i]为true时,i顶点在A组,flag[i]为false时,i顶点在B组。用dist[i][j]记录顶点i至顶点j的距离,初始时,有边相连的两个顶点dist[i][j]的值为图上两点的边权值,无边相连的两个点的dist[i][j]值为无穷大;
代码如下:
#include<stdio.h>
const int max=100000;
int dist[max][max];
bool flag[max];
void main(void)
{
int i,j,n,vi;
int tmp1,tmp2,tmp3;
scanf("%d%d",&n,&vi);//读取所求的图的点的个数及起点位置
while(scanf("%d%d%d",&tmp1,&tmp2,&tmp3))//读取两个点之间的距离,tmp3暂存距离
{
dist[tmp1][tmp2]=tmp3;
dist[tmp2][tmp1]=tmp3;
}
for(i=1;i<=n;i++)
flag[i]=false;
for(i=1;i<=n;i++)//初始时构造dist[i,j]
for(j=1;j<=n;j++)
if(!dist[i][j])
dist[i][j]=max;
//计算顶点vi到其他顶点的最短路径
flag[vi]=true;
for(i=2;i<=n;i++)//求另外n-2个顶点
{
tmp1=max;
for(j=1;j<=n;j++)//找出存在于B组且离vi最近的顶点
if(!flag[j]&&dist[vi][j]<tmp1)
{
tmp1=dist[vi][j];//tmp1记录大小,tmp2记录位置
tmp2=j;
}
flag[tmp2]=true;//将选取的点放入A组
for(j=1;j<=n;j++)//以找到的点为中间点更新vi到B组中的顶点的最短距离
if(!flag[j]&&dist[vi][tmp2]+dist[tmp2][j]<dist[vi][j])
dist[vi][j]=dist[vi][tmp2]+dist[tmp2][j];
}
for(i=1;i<=n;i++)
if(i!=vi)
printf("%d: %d\n",i,dist[vi][i]);//输出vi到各顶点的最短距离
}
举个例子
输入如图所示的顶点关系: