求最短路径的算法总结

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算法适合稀疏图,可以解决负权边

猜你喜欢

转载自blog.csdn.net/waectr/article/details/81256933
今日推荐