最短路径之BFS(宽度优先搜索)+优先队列(Dijkstra算法)c++/c语言

        首先,最短路径问题在树以及树的遍历:宽度优先搜索BFS c++的代码实现-CSDN博客中的最后一道例题中讲过,但这只是最短路径问题中边权为1的特殊的分支之一。读者可以在阅读本篇之前思考一下,如果边权不为1的时候该怎么编码,只用普通的BFS(queue)可以实现吗?

        最短路径问题分很多种,其中最为特殊的也就是边权为1的最短路径问题,其次,还有边权为0或1的,那么这时候就需要要用到双端队列了(deque),但这并不是本篇的主题。本篇要讲的是边权为3,6,4之类的最短路径问题,也就是BFS(宽度优先搜索)+优先队列。

        其实BFS(宽度优先搜索)+优先队列我们也可以将其看作是Dijkstra算法,但两者之间还是有些许的区别,当优先队列中的优先级是距离起点的最小值时,那么BFS(宽度优先搜索)+优先队列=Dijkstra算法,我们甚至可以延申出A*算法以及IDA*算法等。

        最短路径问题也有很多解法,但最主要的解法还是BFS(宽度优先搜索)+优先队列。接下来,我们以下图为例。

        

        (图片来源:shangxueba.com)

        我们以a为起点,g为终点,那么a与g的最短路径是那一条呢?

        我们先想一下,是否可以使用贪心最优搜索呢?每次都走当前边权最短的路径,一直到终点g。但是贪心最优搜索的缺点很明显,不一定能得到正确答案。比如当走到c时,下一个选择走的路径是d,而不是f,但这并不是最优解。毕竟运用了贪心算法,只看当下,不管未来,而且选择后不会回头。所以尽管贪心最优路径的效率很高,但结果不一定正确。

        那用BFS(宽度优先搜索)+优先队列怎样来解呢?

        其实很简单,在前面也说了,优先队列的优先级是当前节点距离起点的距离,也就是Dijkstra算法。我们只需要从起点开始扩展邻节点,并将邻节点的路径重新扫描一次,如果更短则更新该邻节点的优先级,也就是该邻节点距离起点的最短距离。

        比如,在最开始时a入队,扩展邻节点b,e,d,距离分别为1,9,5,a出队。接着选择距离起点最短的b,再扩展新节点c,距离为5,b出队。接下来最短的是c,扩展新节点g,f,距离为15,11,再判断旧节点e,距离为12,大于e的距离,舍去,c出队。然后经过一系列的操作后,最后,当f为队顶时,由于终点g的距离更新为14,得出答案。

        其实,如果只是求一个节点的最短路径用A*算法是最快的,毕竟A*算法用到了估计函数。但如果是求全部节点的最短路径,那么Dijkstra算法比A*更好。

        讲完原理后我们以蓝桥杯的一道例题为例。

        最短路径

        问题描述:给出一个图,求1到其他所有点的最短路径。

        输入:第1行输入正整数n,m,n为点的数量,m为边的数量;第2~m+1行中,每行输入三个数字u,v,w,表示u到v有一条长度为w的单向边。

        这题是典型的BFS+优先队列的题目,该题与上述基本相等,可以直接编写,以下是代码。

#include<iostream>
#include<queue>
using namespace std;
#define N 100010
const long long INF=0x3F3F3F3F3F3F3F;
struct dot{
	int s,t;
	long long w;
	void full(int a,int b,long long w){
		s=a;
		t=b;
		w=w;
	}
};
struct node{
	int id,n_dis;
	void fill(int i,int dis){
		id=i;
		n_dis=dis;
	}
	bool operator <(node &a){
		return this->n_dis > a.n_dis;
	}
};
vector<dot> mp[N];
long long dis[N];
bool vis[N];
void Dijkstra(){
	priority_queue<node> q;
	int s=0;
	dis[s]=0;
	vis[s]=true;
	node now;
	now.fill(s,dis[s]);
	q.push(now);
	while(!q.empty()){
		now=q.top();
		q.pop();
		if(vis[now.id])continue;
		vis[now.id]=true;
		for(int i=0;i<mp[now.id].size();i++){
			dot u=mp[now.id][i];
			if(vis[u.t])continue;
			if(dis[u.t]>u.w+now.n_dis){
				dis[u.t]=u.w+now.n_dis;
				node x;
				x.fill(u.t,dis[u.t]);
				q.push(x);
			}
		}
	}
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++){
		vis[i]=false;
		dis[i]=INF;
	}
	for(int i=0;i<m;i++){
		int a,b,w;
		dot p;
		p.full(a,b,w);
		cin>>a>>b>>w;
		mp[a].push_back(p);
	}
	Dijkstra();
	for(int i=0;i<n;i++){
		if(dis[i]>=INF)cout<<"-1";
		else cout<<dis[i]<<" ";
	}
	return 0;
}

        其中,21行的<号的定义必不可少,因为STL提供的priority_queue,系统的优先级是按照从小到大的,我们只需要将<的定义重载为>就好了。如果理解不了的,也可以将<的重载删去,再在后面直接使用priority_queue<node,vector<node>,greater<node>>代替。

        vis数组是判断该点是否走到最短路径的布尔数组。mp则是用来存储图的。总体来说编码不难。

        如果我没有记错的话,最短路径问题还可以用动态规划,但蒟蒻不太熟悉动态规划,所以在这不多介绍,感兴趣的家人们可以上网找一下。

        打字不易,家人们支持一下吧,本宝宝求求了。

(侵删)

  • 32
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
BFS宽度优先搜索算法常被用于解决迷宫问题中的最路径问题。该算法基于队列的实现方式,可以快速搜索出从起点到达终点的最路径。 具体来说,BFS算法的步骤如下: 1. 创建一个队列,并将起点加入队列中。 2. 创建一个二维数组来记录迷宫中每个位置的状态,例如是否已被访问过或者是墙壁等。 3. 使用循环来遍历队列中的元素,直到队列为空。 4. 在循环中,首先从队列中取出一个节点,并标记该节点为已访问。 5. 然后检查该节点的上、下、左、右四个方向的相邻节点,如果相邻节点是可行的且未被访问过,则将其加入队列中,并记录其距离起点的步数。 6. 重复步骤4和步骤5,直到找到终点或者遍历完所有可行的节点。 7. 如果找到终点,则可以通过回溯找到最路径。 需要注意的是,在BFS算法中,我们需要用一个二维数组来记录每个节点的状态,这样可以避免重复访问节点和处理墙壁等不可行的节点。 总结起来,BFS算法通过队列的方式,逐层遍历迷宫中的节点,直到找到终点。在遍历过程中,通过记录每个节点距离起点的步数,可以求解出最路径。 参考文献: BFS 算法是一种基于队列实现的搜索算法,常用于解决图论问题和迷宫问题。 BFS(Breadth First Search,广度优先搜索)是一种基于队列实现的搜索算法,常用于解决图论问题和迷宫问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值