几大最短路径算法简述

1

Floyd—Warshall算法

Floyd-warshall算法过程简述:时间复杂度O(n^3)
若A点到B点直接举例不为最短距离,则他们之间必有中间点,首先假设点K为中间点,若A->K+K->B小于A->B则将A->B更新为
A->K+K->B,遍历所有两点间的组合;
以(K+i)(1<i&&k+i<=n)点作为中间点循环时,由于两点间的距离以更新为在前K个点范围内的最短距离,所以K所在的循环结束时,任何两点间的距离为最短距离。
此算法可以处理负权边,但是可能不能处理负权回路,原因是负权回路两点间可能没有最短路径。如:1->2=2;2->3=5;3->1=-6;每循环一遍这个环,最短路径就减1.

#include <stdio.h>
#include <stdlib.h>
#define infinity 99999999
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	int e[10][10],k,i,j,n,m,t1,t2,t3;
	scanf("%d%d",&n,&m);//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]=infinity;
		}
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&t1,&t2,&t3);
		e[t1][t2]=t3;
	 }
	 //Floyd-Warshall核心算法 
	 for(k=1;k<=n;k++)
	 	for(i=1;i<=n;i++)
		 	for(j=1;j<=n;j++)
			 	if(e[i][k]<infinity&&e[k][j]<infinity&&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("%d->%d的最短路径:%d\n",i,j,e[i][j]);
			}
	 } 
	return 0;
} 

2

Djistra算法

Djistra算法简述:时间复杂度O(n^2)
Djistra算法求得是源点到各顶点间的最短路径,与Floyd算法不同(求的是各点之间的最短路径),但是后者优良的可扩展性使得它的应用更为广泛。每次循环确定一条最短的边,并更新这个顶点的出边相连的顶点到源点的距离。

#include <stdio.h>
#define inf 99999999
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
	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);
		e[t1][t2]=t3;
	}
	
	for(i=1;i<=n;i++)
		dis[i]=e[1][i];
	
	//book数组初始化
	for(i=1;i<=n;i++)
		book[i]=0;
	book[1]=1;
	//Dijkstra算法核心语句
	for(i=1;i<=n;i++)
	{
		min=inf;
		for(j=1;j<=n;j++)
		if(dis[j]<min&&book[j]==0)
		{
			min=dis[j];
			u=j;
		}
		book[u]=1;//dis[u]为确定值
		for(v=1;v<=n;v++)
		{
			if(e[u][v]<inf)//u-v是一条u点的出边 
			{
				if(dis[v]>dis[u]+e[u][v])
				dis[v]=dis[u]+e[u][v];
			}
		 } 
	 }
	for(i=1;i<=n;i++)
	printf("1->%d:%d\n",i,dis[i]);
	getchar();
	return 0;
}

Bellman-Ford算法

Bellman-Ford算法简述:
此算法的时间复杂度为O(mn),比Djistra的复杂度高,但这是最差的情况,实际上在循环中可以判断是否到达了dis数组中储存的都是最短路径的地步,
可以提前退出(图中无负权回路),若有负权回路,就会运行到k=n-1,不过迪杰斯特拉也解决不了带负权边的图。

#include <stdio.h>
#define inf 99999999
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	int dis[10],bak[10],i,j,k,n,m,u[10],v[10],w[10],check,flag;
	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<=n;i++)
		dis[i]=inf;
	dis[1]=0;
	//Bellman-Ford核心语句。
	for(k=1;k<=n-1;k++)//路径长度最多为通过n-个点,如果存在回路,即此回路为负权回路,无最短路径 
	{
		check=0;
		//新一轮松弛 
		for(i=1;i<=m;i++)
		{
			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("此图含有负权回路\n");
	 else{
	 	for(i=1;i<=n;i++)
	 		printf("1->%d:%d\n",i,dis[i]);
	 }
	getchar();
	return 0;
}

邻接表的使用

邻接表在稀疏图的时候比邻接矩阵的使用时间复杂度更小,前者为O(),后者为O();
以下算法是邻接表的遍历应用
算法思想简述:
此算法总体思想类似于链表,但是每次插入数据是在“表头”而非“表尾”,first数组的下标表示几号顶点,存的数据表示在数组u,v,w,的第几个数据,同时(由于使用的是
next【i】)所以也同时指向了next的第i个数据;当同样的起点路线再次出现时,next【i】存入原本在first中的数据,first存入新数据;以此类推,完成类似于链表的操作,
为遍历相同起点路线提供了便利。
如果图为一个稀疏图的话,用接邻表比用接邻矩阵储存要更好,前者时间复杂度为O(M+N)log(N),后则为O(n^2)。

#include <stdio.h>
#define inf 99999999
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	int n,m,i,k;
	int u[6],v[6],w[6]; 
	int first[5],next[6];
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
		first[i]=-1;//初始化为-1,表示1~n顶点都没有相连的边 
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u[i],&v[i],&w[i]);//起点,终点,两点间距离 
		//关键语句 
		next[i]=first[u[i]];
		first[u[i]]=i;
	}
	//遍历
	for(i=1;i<=n;i++)
	{
		k=first[i];
		while(k!=-1)
		{
			printf("起点:%d  终点:%d  距离:%d\n",u[k],v[k],w[k]);
			k=next[k];
		}
	 } 
	getchar();
	return 0;
}

Bellman-Ford Alogrithm(队列优化)

算法思想简述:
由Bellman-Ford算法的过程可以看出,每次松弛其实只与上次松驰过的顶点相连的边有关,所以没有必要遍历所有顶点查找可以松弛的边,使用队列将松弛过的顶点入队,
查找与顶点相连的可松弛的边,每个顶点只能入队一次 (有点像广搜啊,按照一个源点拓展,有序地遍历所有顶点)。

#include <stdio.h>
#include <string.h>
#define inf 99999999
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	int dis[10],book[10]={0},i,j,k,n,m,u[10],v[10],w[10],first[10],next[10],check,flag;
	scanf("%d%d",&n,&m);
	memset(first,-1,sizeof(int)*10);
	for(i=1;i<=m;i++)
	{
		scanf("%d %d %d",&u[i],&v[i],&w[i]);
		next[i]=first[u[i]];
		first[u[i]]=i;
	}
	for(i=1;i<=n;i++)
		dis[i]=inf;
	dis[1]=0;
	int head=1,tail=1,que[101]={0};//创建队列
	que[head]=1;tail++;//源点入队 
	
	while(head<tail)
	{
		k=first[que[head]];
		while(k!=-1)
		{
			if(dis[v[k]]>dis[u[k]]+w[k])
			{
				dis[v[k]]=dis[u[k]]+w[k];
				
				if(book[v[k]]==0)
				{
					que[tail]=v[k];
					tail++;
					book[v[k]]=1;
					}	
			}
		k=next[k];
		}
	book[que[head]]=0;
	head++;//出队 
	}
	for(i=1;i<=n;i++)
	{
		printf("1->%d:%d\n",i,dis[i]);
	}
	getchar();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值