Dijistra算法求单源最短路径
参考文章
:wiki的博文
算法思想:
设G(V,E)是一个带权有向图,把途中第那个点集合V分为两组,第一组为已求出最短路径的顶点集合(用S表示,初始时只有一个源点v,以后每求一条最短路径,就将加入到集合S中去,直到所有顶点都加入到集合S中去,算法就结束了),第二组为其余未确定的最短路径的集合(用U表示),按最短路径长度的递增次序依次把第二组U中的顶点加入到S中,在加入的过程中,总保持源点v到S中各定点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度,此外,每个顶点对应一个距离,S中顶点的距离就是从v到此顶点的最短路径的长度,U中顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
算法步骤:
a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。
b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
d.重复步骤b和c直到所有顶点都包含在S中。
基本实现思想:
创建一个二维数组,用来存储数据边的长度。
创建两个数组一个S,一个 U ,S用来存储已求出最短路径的集合,U用来存储未求出最短路径的集合
直到S中包含所有的顶点,算法结束。
#include <iostream>
using namespace std;
int main()
{
int e[10][10];//用来存储两点之间的边的长度
int dis[10];//用当前顶点到起始顶点的当前最短路径
int book[10]={0};//标记当前顶点是否找到当前最短路径
int inf=999;//表示路径的最大值
int n,m,start,min,u;
cout<<"输入顶点数和边数"<<endl;
cin>>n>>m;
//初始化
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j) e[i][j]=0;
else e[i][j]=inf;
}
//输入边的相关信息
for(int i=0;i<m;i++)
{
int a,b,value;
cin>>a>>b>>value;
e[a][b]=value;
}
cout<<"输入起点的顶点"<<endl;
cin>>start;
//初始化各顶点到start的距离
for(int i=1;i<=n;i++)
dis[i]=e[start][i];
book[start]=1;//将start顶点最先纳入到集合S中去
//dijstra核心算法
for(int i=1;i<=n-1;i++)//循环n-1次,
{
//找到离start最近的顶点
min=inf;//用来比较得到最近的顶点
for(int t=1;t<=n;t++)
for(int v=1;v<=n;v++)
if(!book[v]&&dis[v]<min)
{
min=dis[v];
u=v;//找出最小值的顶点
}
//将最小值的定点加入到最短路径中
book[u]=1;
//更改U集合中其中的点到start的路径的长度
for(int i=1;i<=n;i++)
{
if(!book[i]&&dis[i]>dis[u]+e[u][i])
{
//交换值
dis[i]=dis[u]+e[u][i];
}
}
}
//输出各顶点到start顶点的最短路径
for(int i=1;i<=n;i++)
{
cout<<start<<"-->"<<i<<"的距离为: "<<dis[i]<<endl;
}
}
测试数据
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
运行结果是
通过上面的代码我们可以看出,这个算法的时间复杂度是 O(N2)。其中每次找到离 1 号顶点最近的顶点的时间复杂度是 O(N),这里我们可以用“堆”(以后再说)来优化,使得这一部分的时间复杂度降低到 O(logN)。另外对于边数 M 少于 N2 的稀疏图来说(我们把 M 远小于 N2 的图称为稀疏图,而 M 相对较大的图称为稠密图),我们可以用邻接表(这是个神马东西?不要着急,下周再仔细讲解)来代替邻接矩阵,使得整个时间复杂度优化到 O( (M+N)logN )。请注意!在最坏的情况下 M 就是 N2,这样的话 MlogN 要比 N2 还要大。但是大多数情况下并不会有那么多边,因此(M+N)logN 要比 N2 小很多。