17-BFS(广度优先搜索算法)

 BFS广度优先搜索算法,是最简便的图搜索算法,常用于解决权重相同且不变的图的最短路求解问题。

广度优先算法(BFS)

BFS广度优先搜索算法),不同于DFS一搜就一直找到叶节点,它的搜索逻辑是一层一层地搜节点,直至找到所需的数据。相较于DFS,BFS占据的空间更大,但是它还是有好处的,我们常用的最短路算法就来源于BFS。

 

下面还是以常见的例子来说明BFS广度搜索算法的应用场景。

迷宫问题

假设我们有下面一个这样的迷宫,它的入口在左上方,出口在右下方,o是可以走的格子、x是不可以走的障碍物,现在我们的任务是要找出从入口到出口所需走到最短距离。

我们可以看到,从入口到出口有很多条路径,但是每两个点之间距离都是1(权重相等),但是红色路径是距离最短的, 其它还有类似绿色路径这样的线路:

 

我们可以尝试使用BFS搜索算法:假设入口第一个点是树的根节点,我们BFS搜索每次搜索下一层的节点可以理解为搜索根节点附近符合条件(可以进行单次移动的点)。为了用代码实现上面的操作,我们首先需要制作一个棋盘和一个dist数组,dist数组它的每个元素存储的是棋盘上对应点到入口节点的距离,而l对组数组存储的是每一步当前点的位置:

#include<iostream>

using namespace std;

const int N = 100;
int n, m;
int chessboard[N][N];//棋盘布局
int dist[N][N];//当前点离起点的距离
pair<int, int> l[N];

int main()
{
	cin >> n >> m;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> chessboard[i][j];
		}
	}
	return 0;
}

在BFS函数里第一步是先将dist数组初始化,即设置每一个点到入口根节点的距离都是-1,除了根节点本身是0。

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
			dist[i][j] = -1;
	}

我们知道从一个点移动到另一个点,每种移动总结起来共有四种情况,向上、向下、向左和向右;我们每个点下次移动都是需要进行四部分的移动,只不过将1、不能过的位置(有x阻碍物)2、之前走过的情况都舍去。这里我们将四种操作具体实例化为两个数组move_x和move_y,move_x[0]、move_y[0]表示左移一个单位;move_x[0]、move_y[1]表示右移一个单位;move_x[1]、move_y[0]表示上移一个单位;move_x[1]、move_y[1]表示下移一个单位;

以后我们每次操作,只需要将当前的位置的横坐标和纵坐标加上对应下表的数组元素就可以表示四种移动

	int move_x[4] = {-1,1,0,0};
	int move_y[4] = {0,0,1,-1};

打算将每一步的当前点都放进一个队列里,所以我们还需要模拟一个队列,当然也可以利用STL里的queue。我队列每次取出对应着我们棋盘上面的的一次移动,这里我们新开一个对组,它存储的是当前移动到的位置now_loct,然后将l数组里面的当前元素赋予给这个对组,再将l数组的队头加加,准备接收下一个新位置。

然后这个for循环就是将四次操作都试一遍,并且每次新的位置都放到另一个新的队组new——loct中。不过只有满足六个条件的新位置才算合适的,才能被放进队列中。六个条件分别是:

1、新的x位置不能越过左边界

2、新的y也不能低于下边界

3、新的x位置不能超过右边界

4、新的y不能高过上边界

5、新的位置必须是棋盘上可走的点

6、新的位置不能是之前走过的

当满足这些条件的新位置才算有效的,于是更新新位置到旧位置之间走过的总距离,并且将新位置放入队列。

while (hh <= tt)
	{
		pair<int, int> now_loct= l[hh++];
		//移动
		for (int p = 0; p < 4; p++)
		{
			pair<int, int>new_loct;
			new_loct.first = now_loct.first + move_x[p];
			new_loct.second = now_loct.second + move_y[p];
			if (new_loct.first >= 0 && new_loct.second >= 0 &&
				new_loct.first < n && new_loct.second < m &&
				chessboard[new_loct.first][new_loct.second] == 0 &&
				dist[new_loct.first][new_loct.second] == -1
				)
			{
				dist[new_loct.first][new_loct.second] = dist[now_loct.first]        [now_loct.second] + 1;
				l[++tt] = { new_loct.first,new_loct.second };
			}
		}
	}

最后返回右下角点(出点)的距离即可。

return dist[n-1][m-1];

所以总的代码如下:

#include<iostream>

using namespace std;

const int N = 100;
int n, m;
int chessboard[N][N];//棋盘布局
int dist[N][N];//当前点离起点的距离
pair<int, int> l[N];

int BFS()
{
	int hh=0, tt = 0;
	l[0] = { 0,0 };
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
			dist[i][j] = -1;
	}
	dist[0][0] = 0;
	int move_x[4] = {-1,1,0,0};
	int move_y[4] = {0,0,1,-1};
	//模拟队列
	while (hh <= tt)
	{
		pair<int, int> now_loct= l[hh++];
		//移动
		for (int p = 0; p < 4; p++)
		{
			pair<int, int>new_loct;
			new_loct.first = now_loct.first + move_x[p];
			new_loct.second = now_loct.second + move_y[p];
			if (new_loct.first >= 0 && new_loct.second >= 0 &&
				new_loct.first < n && new_loct.second < m &&
				chessboard[new_loct.first][new_loct.second] == 0 &&
				dist[new_loct.first][new_loct.second] == -1
				)
			{
				dist[new_loct.first][new_loct.second] = dist[now_loct.first][now_loct.second] + 1;
				l[++tt] = { new_loct.first,new_loct.second };
			}
		}
	}
	return dist[n-1][m-1];
}

int main()
{
	cin >> n >> m;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> chessboard[i][j];
		}
	}
	cout<<BFS();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_61151031/article/details/129915157