Floyd算法
Floyd算法能计算图中任意两个点之间的最短距离,但是耗时较大。它首先遍历每个点 k,再循环遍历一个点作为起点 i,一个点作为终点 j,以前面遍历的点 k 为“踏板/桥”,判断起点 i 到终点 j 的距离是直达(edg[i][j])小还是借“踏板”(edg[i][k]+edg[k][j])到达距离小。然后更新 i 到 j 的距离,从而记录每两个点之间的最短距离。
例如有图:
A - B 2
A - C 6
A - D 8
B - C 3
C - D 1
首先遍历中间点,假设遍历B点为中间点,当遍历到起点为A,终点为C时,edg(A,B)+edg(B,C) <edg(A,C),edg(A,C) = min( edg(A,C),edg(A,B)+edg(B,C) ) = 2+3 = 5,当遍历C点为中间点,遍历到起点为A,终点为D时,edg(A,C)+edg(C,D) < edg(A,D),edg(A,D) = min( edg(A,D),edg(A,C)+edg(C,D) ) = 6+1 = 7。如此往复最后任意对两点 i、j,edg[i][j]得到的是图中点 i 和点 j 之间的最短路,而不一定是刚开始输入的路径权重。
可结合代码理解:
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int main()
{
int e[10][10] , n , m , t1 , t2 , t3;
cin>>n>>m; //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 = 1 ; i <= m ; i ++)
{
cin>>t1>>t2>>t3;
e[t1][t2] = t3;
}
//核心代码
for(int k = 1 ; k <= n ; k ++)//遍历中间点(踏板)
{
for(int i = 1 ; i <= n ; i ++)//遍历起点
{
for(int j = 1 ; j <= n ; j ++)//遍历终点
{
if(e[i][j] > e[i][k] + e[k][j])//比较获得最短路
e[i][j] = e[i][k] + e[k][j];
}
}
}
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 1 ; j <= n ; j ++)
{
printf("%3d",e[i][j]);//最后输出的值为所有点之间的最短路
}
cout<<endl;
}
return 0 ;
}
Dijkstra算法
Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的数组vis,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(源点s不能直接到达的)顶点的路径长度设为无穷大。初始时,数组vis只标记顶点s。 然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到vis中,此时完成一个顶点, 然后,我们需要看看新标记的顶点是否可以到达其他顶点,并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值(dis[i])。 然后,又从dis中找出最小值,重复上述动作,直到vis数组中标记了图的所有顶点。
摘自:https://blog.csdn.net/qq_35644234/article/details/60870719
自己的大概思路及代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define INF 0x3f3f3f3f
/*
https://blog.csdn.net/qq_35644234/article/details/60870719 (迪科斯特算法解析)
迪科斯特算法大概理解思路
创建一个数组存放起点到其他各点的距离,如果与起点没有边,则记为无穷大,创建一个二维数组标记边,一个数组标记与起点相连的点是否已经遍历
首先找到与起点相连的最短路径,然后以此A点为转接点,标记A点,找与此A点相连的B点,比较起点直接到B点与起点到A点后再到B点的距离(时间),
让dis[B]等于最短的那个,依次循环
*/
int dis[105];//记录起点到各点的时间
int vis[105];//标记是否已经确定
int map[105][105];//标记边
int n,m;
void Dijkstra()
{
int i,j,k;
memset(vis,0,sizeof(vis));//初始化 vis
for(i = 1; i<=n; i++)//把起点到各点的时间存入dis
{
dis[i] = map[1][i];
}
vis[1] = 1;
dis[1] = 0;
for(i = 2; i<n; i++)
{
int min = INF;
for(j = 1; j<=n; j++)//找到与起点相连的时间最短的边
{
if(dis[j]<min && !vis[j])
{
min = dis[j];
k = j;//记住该点
}
}
vis[k]++;//标记 k 点已经确定
for(j = 1; j<=n; j++)
{
if(map[j][k]!=INF && !vis[j])//判断是否有和k相连且没有确定的边
{
if(dis[j]>dis[k]+map[j][k])//如果起点直接到j点比以k点为中转再到j点的路径时间长
dis[j] = dis[k]+map[j][k];
}
}
}
}
int main()
{
int i,j,k;
while(~scanf("%d%d",&n,&m))//n为一共有n个点,m表示有m条边
{
if(n ==0 && m == 0)
return 0;
for(i = 1; i<=n; i++)
{
dis[i] = INF;//把起点到各点的时间初始化为无穷大(应该没有必要,在函数里map会赋值给dis)
for(j = 2; j<=n; j++)//把任意两点之间的时间初始化为无穷大
map[i][j] = INF;//可以用memset()初始化
}
while(m--)
{
scanf("%d%d%d",&i,&j,&k);
map[i][j] = k;
map[j][i] = k;
}
Dijkstra();
printf("%d\n",dis[n]);//dis[n]即为起点1到n点的最短路
}
return 0;
}
Floyd算法与Dijkstra算法的不同
1.Floyd算法是求任意两点之间的距离,是多源最短路,而Dijkstra(迪杰斯特拉)算法是求一个顶点到其他所有顶点的最短路径,是单源最短路。
2.Floyd算法属于动态规划,我们在写核心代码时候就是相当于推dp状态方程,Dijkstra(迪杰斯特拉)算法属于贪心算法。
3.Dijkstra(迪杰斯特拉)算法时间复杂度一般是o(n2),Floyd算法时间复杂度是o(n3),Dijkstra(迪杰斯特拉)算法比Floyd算法块。
4.Floyd算法可以算带负权的,而Dijkstra(迪杰斯特拉)算法是不可以算带负权的。并且Floyd算法不能算负权回路。
参考:https://blog.csdn.net/yuewenyao/article/details/81021319