欢迎回到:遇见蓝桥遇见你,不负代码不负卿!

目录

一、广度优先搜索算法(BFS)

典例一:二叉搜索树的范围和

方法一:DFS解法

方法二:BFS解法

典例二:二叉树的层序遍历

典例三:二叉树的层序遍历 II

典例四:岛屿数量

方法一:DFS解法

方法二:BFS解法

五、易错误区

六、蓝桥结语:遇见蓝桥遇见你,不负代码不负卿!

【前言】

搜索算法在蓝桥中考的还是很频繁的,之前发表了二叉树数据结构以及深度优先搜索章节,前面还是比较简单的,这里的广度优先搜索可能稍微复杂那么一丢丢,因为要用到队列,不过我们可以使用STL容器也是很方便就解决了。

【声明】:由于前半部分是基础知识点定义部分,所以前面一小半部分的赘述笔者是参考力扣官方给出的定义以及《算法笔记》一书。

一、广度优先搜索算法(BFS)

对于广度优先搜索的定义及特点,力扣官方是这样给出的:

广度优先搜索算法(Breadth-First Search,缩写为 BFS),又称为宽度优先搜索,是一种图形搜索算法。简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点。广度优先搜索也广泛应用于图论问题中。

齐头并进的广度优先遍历

说明:遍历到一个结点时,如果这个结点有左(右)孩子结点,依次将它们加入队列。

可能上面讲的不够细节,下面详细介绍何为”广搜”:

首先呢,铁汁们先将之前的DFS章节前面的迷宫问题再回顾一下,知道何为“死胡同”以及“岔道口”

https://blog.csdn.net/weixin_57544072/article/details/121262172https://blog.csdn.net/weixin_57544072/article/details/121262172https://blog.csdn.net/weixin_57544072/article/details/121262172

前面介绍了深度优先搜索,可知DFS是以深度作为第一关键词的,即当岔道口时总是先选择其中的一条岔道前进,而不管其他岔路,直到碰到死胡同时才返回岔道口并选择其他岔路。接下来将介绍的广度优先搜索(Breadth First Search, BFS)则是以广度为第一关键词,当碰到岔道口时,总是先依次访问从该岔道口能直接到达的所有节点,然后再按这些节点被访问的顺序去依次访问它们能直接到达的所有节点,以此类推,直到所有节点都被访问为止。

这就跟在平静的水面中投入一颗小石子一样,水花总是以石子落水处为中心,并以同心圆的方式向外扩散至整个水面,从这点来看和DFS那种沿着一条线前进的思路是完全不同的。

概念部分就讲这么多咯,我呢一直是以讲题目练习为主,OK,废话不多说,咱们走起来!

典例一:二叉搜索树的范围和

原题链接:https://leetcode-cn.com/problems/range-sum-of-bst/https://leetcode-cn.com/problems/range-sum-of-bst/https://leetcode-cn.com/problems/range-sum-of-bst/

注意:二叉搜索树的特点就是左子树都比根要小,右子树都比根要大! 

题目描述:

示例1:

输入:root = [10,5,15,3,7,null,18], low = 7, high = 15
输出:32

示例2:

输入:root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10
输出:23

方法一:DFS解法

思路:

本题很简单,铁汁们看代码里面的注释就能理解啦。

代码执行:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/int rangeSumBST(struct TreeNode* root, int low, int high){// //方法一:递归法// //找边界// if(root == NULL){//     return 0;// }// //左子树// int leftSum = rangeSumBST(root->left, low, high);// //右子树// int rightSum = rangeSumBST(root->right, low, high);// int result = leftSum + rightSum;// //判断根节点// if(root->val >= low && root->val <= high){//     result += root->val;// }// return result;//方法二:DFS//判断特殊情况if(root == NULL){return 0;}//如果根节点的值大于high,那么右子树不满足,此时只需要判断左子树if(root->val > high){return rangeSumBST(root->left, low, high);}//如果根节点的值小于low,那么左子树一定不满足,此时只需要判断右子树if(root->val < low){return rangeSumBST(root->right, low, high);}//否则如果根节点的值在low和high之间,那么三者都需要判断return root->val + rangeSumBST(root->left, low, high) + rangeSumBST(root->right, low, high);
}

方法二:BFS解法

思路:

使用广度优先搜索的方法,用一个队列 q 存储需要计算的节点。每次取出队首节点时,若节点为空则跳过该节点,否则按方法一中给出的大小关系来决定加入队列的子节点。

代码执行:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int rangeSumBST(TreeNode* root, int low, int high) {queue<TreeNode*>q;//定义一个队列//首先将根节点入队if(root)q.push(root);int res = 0;while(!q.empty())//队列非空时循环继续{int n = q.size();//队列的长度for(int i = 0; i < n; i++){TreeNode* t = q.front();//访问队首元素q.pop();//队首元素出队//注意输入格式中有空节点,所以要加一个判断//当访问到的节点是空节点时,跳过该节点if(t == nullptr){continue;}//注意哦,由于是二叉搜索树,有它自己的特性//节点的值大于high时,只需要左子树入队if(t->val > high)q.push(t->left);//节点的值小于low时,只需要右子树入队if(t->val < low)q.push(t->right);//节点的值在low和high之间时,需要加上该节点值以及左右子树入队if(t->val >= low && t->val <= high){res += t->val;q.push(t->left);q.push(t->right);}}}return res;}
};

典例二:二叉树的层序遍历

原题链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/https://leetcode-cn.com/problems/binary-tree-level-order-traversal/https://leetcode-cn.com/problems/binary-tree-level-order-traversal/

题目描述:

示例:

思路:

代码中注释给得很详细咯,快去康康叭。

代码执行:

class Solution {
public:/*** * @param root TreeNode* * @return int整型vector<vector<>>*/vector<vector<int> > levelOrder(TreeNode* root) {// write code herequeue<TreeNode*>q;//定义一个队列if(root)q.push(root);vector<vector<int> >ans;//定义一个二维数组用于存放遍历结果while(!q.empty()){//队列为空时停下来int n = q.size();//注意哦,n不能放在循环外边,队列中的元素是在变化的vector<int>tmp;//定义一维数组用于存放每一层的节点(注意一维数组定义的位置)for(int i = 0;i < n;i++){TreeNode* t = q.front();//访问队首元素q.pop();//队首元素出队tmp.push_back(t->val);//将队首元素的值存放到该层的一维数组中if(t->left)//左子节点入队q.push(t->left);if(t->right)//右子节点入队q.push(t->right);}ans.push_back(tmp);//将第一层的一维数组存放二维数组中}return ans;}
};

典例三:二叉树的层序遍历 II

原题链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/

题目描述:

示例:

思路:

哈哈,本题主要是让大家熟练掌握二叉树的层序遍历才添加进来的,本题呢,直接将最后存放到二维数组中的数据反转(#include<algorithm>头文件下)即可。

代码执行:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:vector<vector<int>> levelOrderBottom(TreeNode* root) {//定义一个队列queue<TreeNode*>q;//定义一个二维数组用于返回结果vector<vector<int> >ans;//先将根节点入队if(root)q.push(root);while(!q.empty()){//定义一个一维数组用于存放每一层节点的值vector<int>temp;int n = q.size();//队列的长度for(int i = 0; i < n; i++){//访问队首元素TreeNode* t = q.front();//队首元素出队q.pop();//将队首元素的值存放到一维数组中temp.push_back(t->val);//访问左子树if(t->left)q.push(t->left);//访问右子树if(t->right)q.push(t->right);}ans.push_back(temp);}reverse(ans.begin(), ans.end());//反转二维数组中的结果return ans;}
};

典例四:岛屿数量

原题链接:https://leetcode-cn.com/problems/number-of-islands/https://leetcode-cn.com/problems/number-of-islands/https://leetcode-cn.com/problems/number-of-islands/

题目描述:

示例1:

输入:grid = [["1","1","1","1","0"],["1","1","0","1","0"],["1","1","0","0","0"],["0","0","0","0","0"]
]
输出:1

示例2:

输入:grid = [["1","1","0","0","0"],["1","1","0","0","0"],["0","0","1","0","0"],["0","0","0","1","1"]
]
输出:3

方法一:DFS解法

思路:

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0(也就是下面代码中所说的“同化”)

代码执行:

int numIslands(char** grid, int gridSize, int* gridColSize){//找递归边界if(grid == NULL || gridSize == 0){return 0;}int row = gridSize;//行数int col = *gridColSize;//列数int count = 0;//用于计数int i = 0;int j = 0;//遍历这个二维网格for(i = 0; i < row; i++){for(j = 0; j < col; j++){if(grid[i][j] == '1'){count++;//将‘1’周围的‘1’全部同化成0dfs(grid, i, j, row, col);}}}return count;
}void dfs(char** grid, int x, int y, int row, int col)
{//判断特殊情况if(x < 0 || x >= row || y < 0 || y >= col || grid[x][y] == '0')//注意哦,下标等于行数列数时也是不可以的哟{return;}grid[x][y] = '0';//将‘1’同化成0dfs(grid, x - 1, y, row, col);dfs(grid, x + 1, y, row, col);dfs(grid, x, y - 1, row, col);dfs(grid, x, y + 1, row, col);
}

方法二:BFS解法

思路:

同样地,我们也可以使用广度优先搜索代替深度优先搜索。

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则将其加入队列(注意哦,是将其对应的下标存放到队列中的)开始进行广度优先搜索。在广度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。直到队列为空,搜索结束。

代码执行:

//由于需要用到queue和pair容器,所以选择C++编写代码
class Solution {
public:int numIslands(vector<vector<char>>& grid) {int nr = grid.size();//行数if (!nr) return 0;//判断边界情况int nc = grid[0].size();//列数int num_islands = 0;//用于计数//遍历二维网格for (int r = 0; r < nr; ++r) {for (int c = 0; c < nc; ++c) {//满足条件时进来,否则进入下一次循环if (grid[r][c] == '1') {++num_islands;grid[r][c] = '0';//定义一个队列,用于存放下标信息//注意对pair的理解,可以看作是内部有两个元素的结构体queue<pair<int, int>> neighbors;neighbors.push({r, c});//将'1'的下标信息入队while (!neighbors.empty()) {pair<int,int> rc = neighbors.front();//访问队首元素neighbors.pop();//队首元素出队int row = rc.first;//队首元素所对应的行号int col = rc.second;//队首元素所对应的列号//将它上下左右的‘1’都同化成‘0’//上//row - 1 >= 0 判断位置是否合法if (row - 1 >= 0 && grid[row-1][col] == '1') {neighbors.push({row-1, col});grid[row-1][col] = '0';}//下//row + 1 < nr 判断位置是否合法if (row + 1 < nr && grid[row+1][col] == '1') {neighbors.push({row+1, col});grid[row+1][col] = '0';}//左//col - 1 >= 0 判断位置是否合法if (col - 1 >= 0 && grid[row][col-1] == '1') {neighbors.push({row, col-1});grid[row][col-1] = '0';}//右//col + 1 < nc 判断位置是否合法if (col + 1 < nc && grid[row][col+1] == '1') {neighbors.push({row, col+1});grid[row][col+1] = '0';}}}}}return num_islands;}
};

五、易错误区

最后需要指出的是,当使用STL的queue时,元素入队的push操作只是制造了该元素的一个副本入队,因此在入队后对原元素的修改是不会影响队列中的副本,同样的,对队列中副本的修改也不会改变原元素,需要注意由此可能引入的bug!

例如下面这个例子:

#include<cstdio>
#include<queue>using namespace std;struct node
{int data;
}a[10];int main()
{queue<int> q;for (int i = 1; i <= 3; i++){a[i].data = i;//a[1] = 1, a[2] = 2, a[3] = 3q.push(a[i]);}//尝试直接把队首元素(即a[1])的数据域改为100q.front().data = 100;//事实上对队列元素的修改无法改变原元素printf("%d %d %d\n", a[1].data, a[2].data, a[3].data);//输出1 2 3 注意哦,并不是100 2 3//尝试直接修改a[1]的数据域为200(即a[1],上面已经修改为100)a[1].data = 200;//事实上对原元素的修改也无法改变队列中的元素printf("%d\n", q.front().data);//输出100 注意哦,并不是200return 0;
}

发现上面出现的问题了吗,这就是说,当需要对队列中的元素进行修改而不仅仅是访问时,队列中存放的元素最好不要是元素本身,而是它们对应的编号(如果是数组的话则是下标)。

例如把上面的程序改成下面这样:

#include<stdio.h>
#include<queue>
using namespace std;struct node
{int data;
}a[10];int main()
{queue<int> q;//q存放数组中元素的下标for (int i = 1; i <= 3; i++){a[i].data = i;//a[1] = 1, a[2] = 2, a[3] = 3q.push(i);//这里是将数组下标i入队,而不是节点a[i]本身}a[q.front()].data = 100;//q.front()为下标,通过a[q.front()]即可修改原元素printf("%d\n", a[1].data);//输出100return 0;
}

六、蓝桥结语:遇见蓝桥遇见你,不负代码不负卿!

搜索的基础部分到这里就结束咯,不过嘞,不会这么简单就结束掉的,后面的话笔者还会出一个蓝桥杯冲刺专栏,还有大量的练习以及相当一部分的真题!OK,今天就到这里咯,下一章节讲的是动态规划(DP)哈。

如果大家有所收获的话,麻烦给俺个三连呗,万分感谢,抱拳了哈。

蓝桥杯算法竞赛系列第八章——提高篇之广度优先搜索(BFS)相关推荐

  1. 蓝桥杯算法竞赛系列第0章——蓝桥必考点及标准模板库STL(上)(万字博文,建议抱走)

    欢迎来到:遇见蓝桥遇见你,不负代码不负卿! 目录 ​ 一.蓝桥必考点剖析 二.什么是STL 三.vector的常见用法详解 1.vector的定义 2.vector容器内元素的访问 (1).通过下标访 ...

  2. 蓝桥杯算法竞赛系列第二章——深入理解重难点之递归(上)

    铁汁们,递归(下)已经更新咯,欢迎铁汁们批评指正. 蓝桥杯算法竞赛系列第二章--深入理解重难点之递归(下)_安然无虞的博客-CSDN博客 目录 一.递归是什么? 二.如何理解"递归" ...

  3. 蓝桥杯算法竞赛系列第一章——位运算的奇巧淫技及其实战

    遇见蓝桥遇见你,不负代码不负卿! 第二章"递归"已将更新咯,欢迎铁汁们点评!蓝桥杯算法竞赛系列第二章--深入理解重难点之递归(上)_安然无虞的博客-CSDN博客 目录 一.位运算符 ...

  4. 蓝桥杯算法竞赛备考算法归纳总结

    蓝桥备考基础算法归纳 暴力.贪心 递归.递推 二分.快排 深度优先搜索.广度优先搜索.回溯 字符串处理 双指针 动态规划 各类背包问题 数论 全排列.组合 素数.最大公约数.最小公倍数.欧几里得gcd ...

  5. ((蓝桥杯 刷题全集)【备战(蓝桥杯)算法竞赛-第6天(动态规划 专题)】( 从头开始重新做题,记录备战竞赛路上的每一道题 )距离蓝桥杯还有61天

  6. [蓝桥杯][算法提高VIP]夺宝奇兵-递推+记忆化搜索

    题目描述 在一座山上,有很多很多珠宝,它们散落在山底通往山顶的每条道路上,不同道路上的珠宝的数目也各不相同.下图为一张藏宝地图: 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 " ...

  7. [蓝桥杯][算法提高VIP]夺宝奇兵-dp

    题目描述 在一座山上,有很多很多珠宝,它们散落在山底通往山顶的每条道路上,不同道路上的珠宝的数目也各不相同.下图为一张藏宝地图: 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 " ...

  8. JAVA 蓝桥杯 算法提高 阮小二买彩票

    JAVA 蓝桥杯 算法提高 阮小二买彩票 资源限制 时间限制:1.0s 内存限制:512.0MB 问题描述 在同学们的帮助下,阮小二是变的越来越懒了,连算账都不愿意自己亲自动手了,每天的工作就是坐在电 ...

  9. [蓝桥杯][算法提高VIP]阮小二买彩票

    [蓝桥杯][算法提高VIP]阮小二买彩票 题目描述 在同学们的帮助下,阮小二是变的越来越懒了, 连算账都不愿意自己亲自动手了,每天的工作就是坐在电脑前看自己的银行账户的钱是否有变多.可是一段时间观察下 ...

最新文章

  1. perl XML创建XML文件
  2. 1.75亿美元!吴恩达第三锤:宣布成立AI基金AIFund
  3. Hbase完全分布式的搭建
  4. 多领导者改进算法的MATLAB仿真
  5. Django框架----分页器(paginator)
  6. js的字符串和变量拼接
  7. iostext添加点击事件_iOS开发小技巧 - label中的文字添加点击事件
  8. matlab 写excel 慢_我在12w+的Python库中,发现了让Excel快到起飞的秘密......
  9. 相见恨晚!遗憾仅有不到1% 的人知道
  10. 面试官:你说说互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景?
  11. 信息学奥赛一本通(1076:正常血压)
  12. Python学习第四天
  13. 130242014039-(2)-体验敏捷开发
  14. vue 项目完美运行在IE或者360浏览器兼容模式下 踩坑笔记
  15. Controlling the Amount of Verbatim Copying in Abstractive Summarization
  16. Linux下网络传输(模拟路由器)
  17. 使用Synopsys VCS使用constraint遇到的一个奇怪问题
  18. 数字化时代,如何推动实体经济和数字经济的融合
  19. 英语app二维码及图标
  20. uim详解-5(卡上操作系统cos)

热门文章

  1. 在AR9331上使用Openwrt 默认开启wifi
  2. taro微信小程序时间组件picker的使用--省市区三级联动
  3. [BCI]Neuralink 与大脑的神奇未来-读书笔记
  4. [转]论语新解(上篇)(2)
  5. 社工小组 计算机小组活动,社工小组活动计划书
  6. Github每日精选(第65期):手机自动化测试工具maestro
  7. 有限元法的学习(一)
  8. [转贴]美丽的童话——游戏学院真相探访
  9. 元宇宙:我所知道的一切
  10. MES制造执行系统设计与开发