最短路是图论最重要的算法之一,也是算法难点。经过这篇的学习,你会发现你离成功就差一个最短路的距离。=.=
最短路是指,某个点到某个点之间的距离最短。
算法1:Dijkstra算法
迪杰斯特拉算法是很典型的单源最短路算法,而且时间复杂度较其他算法更低,唯一的缺点是无法判断含负权边的图的最短路。而且其仅能作为单源最短路,也就是一个起点、一个终点求最短。在开始前推荐大家先去看我的另一篇博客最小生成树其中的prim算法和这个很类似。
同样由于技术有限,仅靠文字和代码讲解。-_-
int pic[1005][1005];//存图
在输入之前,初始化图,将各点之间的距离设置为最大值,表示无法到达。
这里的图是单向的。
void init()
{
for(int i=0;i<1005;i++)
{
for(int j=0;j<1005;j++)
{
pic[i][j]=(1<<21);//最大值可以自己设定,只要能表示不可达即可
}
}
}
dijkstra模板函数如下
特别强调c++和java不同,新开的数组会沿用之前相同名字的内存地址,所以赋值false很关键。之前我的模板错误在此
另外注意起点下标0-n-1或者1-n需要更改
void dijkstra()
{
int weight[1005];//起始到其他端点的值
bool visit[1005];
for(int i=0;i<n;i++)
{
weight[i]=pic[0][i];//将图中0到各个端点的值赋给weight数组
visit[i]=false;
}
weight[0]=0;
visit[0]=true;
//此时起点为0
while(true)//0点作为起始点,从下一个点开始
{
int j=0,k=-1,min=(1<<21);
//weight内的值为0时表示,已经访问过
//遍历weight数组,寻找一个最小值,并且该点没有被访问过
while(j<n)
{
if(min>weight[j]&&!visit[j])
{
min=weight[j];
k=j;//用k存下这个节点
}
j++;
}
//已知的min是我当前起始节点到k节点的距离,已知连接边中最短的
if(k==-1) break;
visit[k]=true;//访问过该节点
//自此形成从起始到K的边,将其连接
for(int m=0;m<n;m++)
{
if(weight[m]>pic[k][m]+weight[k]&&!visit[m])
{
weight[m]=pic[k][m]+weight[k]; //将单点间距离与新更新值距离比较求最小
}
}
}
}
和prim唯一一点不同的是,其他点存的距离为一段距离,未连接之前是最大值,连接过后就是某一段加某一段,weight数组里面存的是起始点到各点之间的距离。
- 技巧:当你需要多源最短路而苦于Floyd的算法复杂度时,你不妨换种思路,把终点当做起点,而你需要求的其他顶点到终点的距离自然就有了。
算法2:Floyd算法
唯一的缺点就是时间复杂度过高(On^3),在算法实现时需要注意arr[i][k]+arr[k][j]的溢出问题所以可能需要long long数组。
void floyd()//算法模板
{
for(int k=0;k<=n;k++)
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
if(arr[i][j]>arr[i][k]+arr[k][j])
arr[i][j]=arr[i][k]+arr[k][j];
}
n表示点的个数,arr里面存取的是点到点的路径值,经过算法计算后,点到点的距离即为最小,可以求各个点之间的。
算法3:bellman-ford算法
之前遗漏,有待补充