6.2 最短路径:Dijkstra算法与单源最短路径
Dijkstra算法——单源最短路径
Dijkstra算法是用来处理"指定一个点,计算该点到其余各个顶点的最短路径"这件事
简介
和上文有点像,因为当我们讨论一点到各个点的距离的时候,我们就不得不计算各种中转站
我们这里得到了一个新定义,松弛,我们认为它是:
如果两点距离通过中转点缩短了距离,我们就把这个过程叫松弛
Dijkstra算法实际就是 “选点,松弛,更新,选点” 的不断循环,直到得到结果
核心
我们这里使用两个数组
二维数组e来存储两点之间的路径关系
一维数组dis来存储一个点到其余各个点的初始路程(接下来会以1当例子)
我们暂且把dis中的值叫做“估计值”
我们先选择离1最近的点,得到其距离,和“估计值”比较,得到“确定值”
比如由1得到2,再由2得到其它边
然后更新、比较数据
步骤
1.将所有顶点分为两个部分。已知最短路径的集合P,未知的集合Q。
最开始P中只有源点一个顶点。我们可以用book数组来标记已知点
2.设置源点到自己的路径为0.
如果存在源点能直接到达的顶点i,则设置dis[i]为e[s][i]
把不能直接到达的顶点,设dis[i]为正无穷
3.在集合Q中选择一个离源点最近的顶点u(dis[u]最小)加入到P
检查所有以u为起点的边,每一条边都进行一次松弛的检查
边长为dis[u]+e[u][v],比较这个值和dis[u]的大小
4.重复第三步,直到Q为空,算法结束
代码示例
#include <stdio.h>
int main()
{
int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
int inf = 99999999 ;
scanf("%d %d",&n,&m);
//信息初始化
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(i==j)
e[i][j] = 0 ;
else
e[i][j] = inf ;
//读入边
for(i=1;i<=n;i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
//初始化dis数组,这是1号顶点到达各个顶点的路程
for(i=1;i=n;i++)
dis[i] = e[1][i];
//book数组初始化
for(i=1;i<n;i++)
book[i] = 0 ;
book[1]=1;
//Dijkstra算法的核心语句
for(i=1;i<=n-1;i++)
{
//找到离1最近的顶点
min = inf ;
for(j=1;j<-n;j++)
{
if(book[j]==0 && dis[j]<min)
{
min = dis[j];
u = j ;
}
}
book[u] = 1 ;//标记该点
for(v=1;v<=n:v++)
{
if(e[u][v] < inf)
{
//比较与数据更新
if(dis[v] > dis[u] + e[u][v])
dis[v] = dis[u] + e[u][v];
}
}
}
//输出最终结果
for(i]1;i<=n;i++)
printf("%d",dis[i]);
getchar();gerchar();
return 0 ;
}
事实上,这是一种基于贪心策略的算法
邻接表
我们可以利用邻接表来存储一个图
int n,m,i ;
//u,v和w的数组大小根据情况来设置,要比m的最大值大1;
int u[6],v[6],w[6] ;
//first和next的数组大小要根据实际情况来设置
//first要比n的最大值大1
//next要比m的最大值大1
int first[5],next[6];
scanf("%d %d",&n,&m);
//初始化first数组的下标1-n的数值为-1,表示1-n顶点暂时都没有边
for(i=1;i<=n;i++)
first[i] = -1;
for(i=1;i<=m;i++)
{
scanf("%d %d %d",&u[i],&v[i],&w[i]) ; //读入每一条边
next[i] = first[u[i]];
first[u[i]] = i ;
}
用u,v,w三个数组来记录每条边的信息,即u[i],v[i],w[i]表示:
第i条边是从第u[i]号顶点到第v[i]顶点(u[i]->v[i]),且权值为w[i]
next[i]存储"编号为i的边"的"下一条边"的编号