最短路径———BFS,Dijkstra和Floyd算法

目录

BFS算法

具体代码

Dijkstra算法

基本思想

具体代码

Floyd算法

基本思想

具体代码


        BFS算法主要用来解决无权图的单源最短路径问题,Dijkstra算法只要解决带权图的单源最短路径问题,Floyd算法主要解决多源最短路径问题

        最短路径问题主要针对有向图而言,主要应用有导航选择时间,或者价格,或者步行最少的路径。(区别于最小生成树:一般针对无向图

单源最短路径:一个点到其他所有点的最短路径

多源最短路径:图中任意两个点的最短路径

BFS算法

时间复杂度:O(V²),图的存储采用邻接矩阵

改编BFS遍历,主要是访问顶点的时候需要对顶点的dist[]数组进行处理

具体代码
#include<bits/stdc++.h>
#include<queue>
#define MAX 0x3f
#define INF 0x3f3f3f3f
using namespace std;

const int N=10010;

int x;    //从x顶点开始 
int m,n;   //m个顶点,n条边
int g[N][N];
int dist[N]; 
int path[N]; //用来记录路径(逆序记录)
bool st[N];
queue<int> q;

void BFS(int x)
{
	q.push(x);
	st[x]=true;
	dist[x]=0;
	while(!q.empty())
	{
		int k=q.front();
		q.pop();
		for(int i=1;i<=m;i++)
		{
			if(!st[i]&&g[k][i]==1)   //不能写成g[k][i],因为g[][]的值是1或者INF,没有0 
			{
				dist[i]=dist[k]+1;	
				st[i]=true;
				path[i]=k;
				q.push(i);
			}	
		}
	}
}

int main()
{
	memset(path,-1,sizeof path); 
	memset(g,MAX,sizeof g);
	memset(dist,MAX,sizeof dist);
	memset(st,false,sizeof st);
	freopen("B.txt","r",stdin);
	cin>>x; 
	cin>>m>>n;
	while(n--)
	{
		int a,b;
		cin>>a>>b;
		g[a][b]=min(g[a][b],1);
	}
	BFS(x);
	for(int i=1;i<=m;i++)
	{
		if(dist[i]==INF)
		{
			cout<<"-1"<<" ";
		}
		else cout<<dist[i]<<" ";
	} 
	cout<<endl;
	int k=8;
	cout<<k<<" ";
	while(path[k]!=-1) //逆序输出1->8的最短路径经过哪些点 
	{
		cout<<path[k]<<" ";
		k=path[k]; 
	}
	cout<<endl;
	return 0;
} 

Dijkstra算法

基本思想

        两个点之间的最短路径包含了两点之前其他点的最短路径。

        用dist[]数组来记录每个点当前到源点的最短距离,从没有确定最短路径的点中找到当前到源点距离最短的点(当前dist[]最小的点)加入到已经确定最短路径的集合中(基于贪心),每次有新的点加入之后,都需要根据新加入的点更新dist[]数组,这样每次选择加入的点就能确定是已经是最短路径。

具体代码
//1->n的最短距离 
#include<bits/stdc++.h>
#define MAX 0x3f
#define INF 0x3f3f3f3f

using namespace std;

const int N=20020;

int g[N][N];
int dist[N];  
int path[N];  //记录路径 
bool st[N];
int m,n;  //m个顶点,n条边 

int Dijkstra()
{
	st[1]=true;   //从源点开始,将源点加入
	for(int i=1;i<=m;i++)   //根据源点修改dist[]数组和path[]数组
	{
		if(g[1][i]!=INF)
		{
			dist[i]=min(dist[i],g[1][i]);
			path[i]=1; 
		}
	}
	for(int i=1;i<m;i++)
	{
		int k=-1;
		for(int j=1;j<=m;j++)
		{
			if(!st[j]&&(k==-1||dist[k]>dist[j]))
			{
				k=j;
			}
		}
		st[k]=true;
		for(int j=1;j<=m;j++)
		{
			if(!st[j])
			{
				if(dist[j]>dist[k]+g[k][j])
				{
					dist[j]=dist[k]+g[k][j];
					path[j]=k;
				}
			}
		}
	}
	if(dist[m]==0) return INF;
	return dist[m];
}

int main()
{
	path[1]=-1;
	memset(g,MAX,sizeof g);
	memset(dist,MAX,sizeof dist);
	memset(st,false,sizeof st); 
	freopen("B.txt","r",stdin);
	scanf("%d%d",&m,&n);
	while(n--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		g[a][b]=min(g[a][b],c);
	}
	int t=Dijkstra();
	if(t!=INF) cout<<t<<endl;
	else cout<<"不存在最短距离"<<endl;
	int a=m;       //逆序输出从1->n的路径
	cout<<a<<" ";
	while(path[a]!=-1)
	{
		cout<<path[a]<<" ";
		a=path[a];
	}
	return 0; 
} 

时间复杂度:O(V²)

和Prim算法(最小生成树(MST)Prim算法和Kruskal算法-CSDN博客)的代码特别像,但是主要区别在Prim算法的lowcost[]数组记录的是当前没有处理的点到当前MST的最短距离。

for(int j=1;j<=m;j++)
{ 
	lowcost[j]=min(lowcost[j],g[k][j]);        //更新没有加入的点的最小代价 
}

而Dijkstra算法的dist[]数组记录的是当前没有确定最短路径的点到到源点的最短距离。

for(int j=1;j<=m;j++)
{
	if(!st[j])
	{
		if(dist[j]>dist[k]+g[k][j])
		{
			dist[j]=dist[k]+g[k][j];
			path[j]=k;
		}
	}
}

Floyd算法

基本思想

        对应任意的两个点vi,vj,可以遍历所有的顶点,让顶点(例如顶点vk)在这两个点之间中转,即vi,vj通过vk能否找到长度更短的路径(即比较g[i][j]和g[i][k]+g[k][j]),如果g[i][j]>g[i][k]+g[k][j],就可以更新g[][]数组,这样不断加入新的中转点,g[][]数组也会在递推过程中不断被更新,当所有的点都参与中转之后得到的g[][]数组中记录的内容就是任意两点之间的最短路径长度。

具体代码

        时间复杂度:O(V³),所有点都需要中转,并且每次中转都需要改变g[][]数组中任意两点之间的当前最短路径长度,即需要遍历数组。

#include<bits/stdc++.h>
#define MAX 0x3f
#define INF 0x3f3f3f3f

using namespace std;

const int N=210;

int g[N][N];   //一开始是邻接矩阵,后来是经过中转之后当前的最短距离 
int path[N][N];

int m,n,k;

void Floyd()
{
	for(int k=1;k<=m;k++)
	{
		for(int i=1;i<=m;i++)
		{
			for(int j=1;j<=m;j++)
			{
				if(g[i][j]>g[i][k]+g[k][j])
				{
					g[i][j]=g[i][k]+g[k][j];
					path[i][j]=k;
				}
			}
		}
	}
}
int main()
{
	memset(g,MAX,sizeof g);
	memset(path,-1,sizeof path);
	freopen("B.txt","r",stdin);
	scanf("%d%d%d",&m,&n,&k);
	for(int i=1;i<=m;i++)   //一开始邻接矩阵的对角线全是0,而不是无穷
	{
	    for(int j=1;j<=m;j++)
	    {
	        if(i==j) g[i][j]=0;
	        else g[i][j]=INF;
	    }
	}
	
	while(n--)
	{
		int x,y,w;
		scanf("%d%d%d",&x,&y,&w);
		g[x][y]=min(g[x][y],w);
	}
	Floyd();
	while(k--)
	{
		int st,ed;
		scanf("%d%d",&st,&ed); 
		if(g[st][ed]<INF/2) cout<<g[st][ed]<<endl;
		else cout<<"-1"<<endl;
	}
	return 0;
} 

注意:初始化g[][]的时候对角线要初始化为0,其他点为INF,有别于其他用到邻接矩阵的算法

g[][]一开始存储的是有向图的邻接矩阵,经过算法递推之后最终变成任意两点之间的最短距离。

至此,最短路算法告一段落。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值