BFS & DFS
DFS(Deep First Search)深度优先搜索
BFS(Breath First Search)广度优先搜索
Content
一、BFS
二、DFS
三、知识拓展——队列
一、BFS
适用场景
一般用于求最优解
核心思想
1. 规定搜索方向
2. 利用二维数组模拟地图,并且多开一张地图用来标记地图是否走过
3. 将起始位置信息存入队列后,取出起始位置信息,再将起始位置所有的搜索方向内存在的可行点依次存入队列,完成当前点所有方向的遍历后,再取出队列内的其它点,按照起始位置的方式依次遍历并存入队列。
4. 我们所有的操作都是为了寻找每个位置点内的信息。而这个点是否为可行点取决于标记地图内存储的信息,当然每次放一个点进入队列都要将这个点所在的标记地图上做标记,以此表明此点已遍历。我们要搜索的信息主要是在我们从队列内取出点是做判断。
思维图解
上图表示搜索此图中有多少红色色块
图中虽然只看到 黄色色块单向移动,但实际上它会以一个点为中心判断它的八个方向,依次是上、左上、左、左下、下、右下、右、右上,如果存在零,就将它标记为1。
注意理解前面的二十个色块的遍历过程就可以了。
每当黄色色块到达的地方会做标记和检测内容,每次到达都会判断当前位置是否储存了红色色块信息,我们在代码中添加一个计数变量,在搜索完图时即可获得图中红色色块的数量信息。
当然,BFS的运用不止于此,还可以迷宫寻路、搜索二叉树等许多操作。
BFS可行代码
int red_number = 0; char mp[105][105]; //开图 int fx[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; //定义运动方向的先后顺序 int fy[8] = {0, 1, 1, 1, 0, -1, -1, -1}; //移动方向是上、左上、左、左下、下、右下、右、右上 bool vis[105][105]; //标记地图的数组 判断是否走过 struct node { int x1, y1; }; //坐标信息 void bfs(int x, int y) { queue<node> q; //定义队空间q,声明该队储存数据为node型 q.push({x, y}); //用push()函数给队空间放入元素 while (!q.empty()) //队空间非空就执行循环 { node now = q.front(); //定义node型变量now并初始化为队空间最靠前的一个数据 //front()函数可以取出队列内最靠前的数据 q.pop() //用pop()函数删除最靠前的一个数据 for (int i = 0; i < 8; i++) { int newx = now.x1 + fx[i]; int newy = now.y1 + fy[i]; //按方向顺序移动位置,检测起点周围的八个方向位置中的信息 if (mp[newx][newy] == 'R' && newx >= 0 && newx < n && newy >= 0 && newy < m && !vis[newx][newy]) //判断地图可行点 在地图内且没有走过 { red_number++; q.push({newx, newy}); //八个符合要求的方向放入队列,用于之后的搜索 vis[newx][newy] = 1; //将遍历过的位置标记为1 } } } return; }
二、DFS
适用场景
用于寻找可行解的个数
核心思想
利用递归按照规定方向寻找目标,当找到不可行路时,会原路返回到上一个岔路口,去找新的路,直到遍历完所有。(看似简单,难以理解 =_=)
思维图解
同样的,每个当前位置,会遍历它的八个方向(与BFS相同)找到可行方向就会继续调用DFS函数如果8个方向被封死,DFS就会退回到上一个DFS(色块回到上一个位置),重新找其它可行点,直到找到终点@(也可以为别的标识符),或者被完全封死。
可行代码
int red_number = 0; char mp[105][105]; //开图 int fx[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; //定义运动方向的先后顺序 int fy[8] = {0, 1, 1, 1, 0, -1, -1, -1}; //移动方向是上、左上、左、左下、下、右下、右、右上 bool vis[105][105]; //标记地图的数组 判断是否走过 void dfs(int x, int y) { for (int i = 0; i < 8; i++) { int newx = x + fx[i]; int newy = y + fy[i]; // 按方向移动 if (mp[newx][newy] == '@' && newx >= 0 && newx < n && newy >= 0 && newy < m && !vis[newx][newy]) // 判断可行点 { vis[newx][newy] = 1; // 标记 dfs(newx, newy); // 函数递归 } } return; }
三、知识拓展——队列
头文件:#include <queue>
队列相当于是一个上下空洞、左右封闭的空间,元素从上面进入队列(用push() 函数添加元素),元素会按照存入循序从下往上排好,当使用front() 函数时仅仅可以读取队列最下面的那个元素,所以通常情况下,我们会在读取元素后用pop() 函数删除队列最下面的元素。
当我们放入的元素不止一个类型时,我们就要考虑使用结构体了,例如我们的BFS在给对立元素时是以坐标形式传入,所以结构体绑定不同的数据一定要掌握。
图解队列
写在最后
学无止尽,Kirk很开心你可以看完这篇文章,不知道对你有没有什么帮助,如果对文章有什么疑惑,或者你看出了文章有什么问题,可以私信KIrk哦,同时也欢迎所有想学习编程的readers前来交流,Kirk对来者都不拒的哦,祝各位readers学习编程愉快!!!
看完文章学完知识可以去练练手脚,这里推荐两个题:
1. 油田问题
2. 迷宫问题
最后奉上全套代码(针对油田问题)
#include <cstring> #include <iostream> #include <queue> //队列 using namespace std; int n, m; char mp[105][105]; int fx[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; int fy[8] = {0, 1, 1, 1, 0, -1, -1, -1}; bool vis[105][105]; struct node { int x1, y1; }; void bfs(int x, int y) { queue<node> q; q.push({x, y}); while (!q.empty()) { node now = q.front(); q.pop(); for (int i = 0; i < 8; i++) { int newx = now.x1 + fx[i]; int newy = now.y1 + fy[i]; if (mp[newx][newy] == '@' && newx >= 0 && newx < n && newy >= 0 && newy < m && !vis[newx][newy]) { //判断地图可行点 q.push({newx, newy}); vis[newx][newy] = 1; } } } return; } void dfs(int x, int y) { for (int i = 0; i < 8; i++) { int newx = x + fx[i]; int newy = y + fy[i]; if (mp[newx][newy] == '@' && newx >= 0 && newx < n && newy >= 0 && newy < m && !vis[newx][newy]) { vis[newx][newy] = 1; dfs(newx, newy); } } return; } int main() { while (cin >> n >> m && n) { int sum = 0; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) cin >> mp[i][j]; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (mp[i][j] == '@' && !vis[i][j]) { vis[i][j] = 1; dfs(i, j); sum++; } } } cout << sum << endl; } return 0; } //BFS:求最优解 //DFS:求解的个数