Floyd-Warshall算法
这个算法所使用的基本思想是:如果我们要求两个点之间的最短距离,不一定是两个点之间的直达距离,有可能是经过转点后的距离,例如从A->B的距离为10,而从A->C的距离为5,从C->B的距离为4,如果我们走A->C->B的距离的话,那么就更短,如果确定这条路径是最短距离,那么也同样可以确定,A->C的最短路径为5,B->C的最短路径为4,也就是说,如果我们确定了一条多转站的路径为最短路径后,它所经过的点都为,从一点到沿途点的最短路径
#include<stdio.h>
int main(){
int e[10][10],k,j,i,n,m,t1,t3,t2;
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<=m;i++){
scanf("%d %d %d",&t1,&t2,&t3);
}
//算法核心语句
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(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(i=1;i<=n;i++){
for(j=1;j<=n;j++){
printf("%10d",e[i][j]);
}
printf("\n");
}
}
Dijkstra算法
他所处理的图不能有负权
这个算法的基本思路是:假设我们已经得到领接矩阵,也就是不同位置的关系表示图,通过这个算法我们会求出1到其他的点的最短路径,通过数组dis[ ]来记下初始化状态下1到其他点的距离,我们先从离源点最近(权值最小)的点开始,搜索他周围能到达的所有的点的路径,将1经过这个点到达他们的总路径相加,与dis[ ]数组中的相对于的点进行比较,如果距离小与dis[]数组中的距离,就更新dis中的距离,在这个离1最近的点找完后,我们再去找下一个离源点近的点,再遍历它周围的所有点,每次找到的离源点最近的点后,都要去标记它已经被使用过了,下次遍历的寻找的时候就不能再 用它了,就这样反复,使用的方法和广度优先搜索相似
#include<stdio.h>
int main(){
int e[10][10],k,j,i,n,m,t1,t3,t2,min,u,v;
int inf=99999999;
int book[10]={0},dis[10]={0};
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<=m;i++){
scanf("%d %d %d",&t1,&t2,&t3);
}
//先得到一号顶点到其他顶点的初始路程
for(i=1;i<=n;i++){
dis[i]=e[1][i];
}
for(i=1;i<=n;i++){
book[i]=0;
}
book[1]=1;
//算法核心
for(i=1;i<=n-1;i++){
//求还没又被使用过的里原点最近的数据
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]);
}
Bellman-Ford算法
如果一个图拥有负权环,就不存在最短路径了,它的核心代码很短,如果一个领接矩阵中存在负权环,,这个算法也是可以计算的,我们先假设它是正权环,我们先进行循环n-1次,循环的n-1次就是正权环有n个顶点的话就最多有n-1条边,这个算法就是来枚举每条边的数据,它的操作会根据所输入的边的次序来进行,每次按所输入边的顺序全部遍历一遍,然后将符合条件的进行操作,知道达到要求的效果,但如果我们遇到负权环的话,我们进行了n-1次操作后,再次对这个图进行Ford算法的操作时,我们发现他还可以操作的。所以我们可以在结尾判断一下,如果可以继续操作的话,那就说它是没有最短路径的
#include<stdio.h>
int main(){
int dis[10],i,k,,n,m,u[10],v[10],w[10];
int inf=99999999;
scanf("%d %d",&n,&m);
for(i=1;i<=m;i++){
scanf("%d %d %d",&u[i],&v[i],&w[i]);
}
//初始化数组
for(i=1;i<=m;i++){
dis[i]=inf;
}
dis[1]=0;
//核心算法
for(k=1;k<=n-1;k++){ //进行n-1次
check=0;
for(i=1;i<=m;i++){ //操作m次所有的边
if(dis[v[i]]>dis[u[i]]+w[i]){
dis[v[i]]=dis[u[i]]+w[i];
check=1;
}
}
if(check==0) break;
}
// 检测负权回路
flag=0;
for(i=1;i<=m;i++){
if(dis[v[i]]>dis[u[i]]+w[i])
flag=1;
if(flag==1) printf("此图是负权回路");
else{
//最终输出
for(i=1;i<=n;i++)
printf("%d ",dis[i]);
}
}
总结一下:
Floyd算法适合稠密图,而且可以解决负权边,但是时间复杂度高
Dijkstra算法适合稠密图,但是不能解决负权边
Forf算法适合稀疏图,可以解决负权边