算法学习(19)—— 队列与 BFS

关于bfs

bfs又称宽搜,全称是“宽度优先遍历”,然后就是关于bfs的三个说法:“宽度优先搜索”,“宽度优先遍历”,“层序遍历”,这三个都是同一个东西,前面我们介绍了大量的深度优先遍历的题目已经衍生算法,对这类比较熟悉的

在前面讲解深搜之前,我们上一篇数据结构相关的文章是介绍栈的,也列举了一些与栈相关的题目:算法学习(十一)—— 栈-CSDN博客

纯队列的题目很少很少,队列这种数据结构大部分都是用来服务BFS算法的,所以我们就把BFS和队列放一起来介绍。

和深搜一样,宽搜不仅仅只应用在树形结构中,这篇文章只列举了部分在树中的应用,先熟悉下BFS算法,后面会再展开细讲 

部分OJ题详解

429. N叉树的层序遍历

429. N 叉树的层序遍历 - 力扣(LeetCode)

第一道题就是简单的开胃菜,题目很简单,就是按顺序返回每一层的节点,下面来分析下这道题:

  • 绝大部分的bfs题的代码类系都差不多,相当于有一个模板。因为从左往右遍历第一层后,要再次从最左边的节点继续往下遍历,所以我们是需要借助队列这个数据结构的,因为队列是先进先出的,遍历最左边节点时需要记录下,以便往下一层遍历时能找到最左边节点
  • 这第一题我们就详细一点介绍下层序遍历的步骤:
  • ①先搞一个队列,然后如果根节点不为空,让根节点入队列
  • ②然后就是一个循环,条件是队列不为空时,先拿出队头元素,把队头元素的所有孩子节点都入队列,然后pop掉队头元素,一直重复这步,直到队列为空,层序遍历完成‘
  • ③然后就是统计元素的时机,每次遍历时,先统计下队列里有多少元素,因为此时队列里元素的个数刚好是这一层元素的个数,然后按照数量执行步骤②即可
class Solution 
{
public:
    queue<Node*> q;
    vector<vector<int>> ret;
    vector<vector<int>> levelOrder(Node* root) 
    {
        bfs(root);
        return ret;
    }
    void bfs(Node* root)
    {
        if(root == nullptr) return;
        q.push(root); //先把根节点入队列
        while(!q.empty())
        {
            int size = q.size(); //记录此时队列元素个数,该个数代表本层节点的个数
            vector<int> v;
            for(size_t i = 0; i < size; i++)
            {
                Node* cur = q.front(); //拿到队头元素
                q.pop();
                v.push_back(cur->val);
                for(auto e : cur->children) //依次把队头下一层节点入队列
                {
                    q.push(e);
                }
            }
            ret.push_back(v);
        }
    }
};

103. 二叉树的锯齿形层序遍历

103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode)

锯齿形遍历,就是先从左往右(包括根节点),再从右往左遍历,依次循环直到遍历完成,其它的和第一题是一样的:

  • 只需要在上一道题的基础上稍加修改即可;只需要把偶数行的结果在添加进最终数组之前,逆序一下即可
class Solution 
{
    queue<TreeNode*> q;
    vector<vector<int>> ret;
    bool e = true;
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) 
    {
        bfs(root);
        return ret;
    }
    
    void bfs(TreeNode* root)
    {
        if(root == nullptr) return;
        q.push(root); //先把根节点入队列
        while(!q.empty())
        {
            int size = q.size();
            vector<int> v;
            for(size_t i = 0; i < size; i++)
            {
                TreeNode* cur = q.front(); //拿到队头元素
                q.pop();
                v.push_back(cur->val);
                if(cur->left) q.push(cur->left);
                if(cur->right) q.push(cur->right);
            }
            if(e == true)
            {
                ret.push_back(v);
                e = false;
            }
            else
            {
                reverse(v.begin(), v.end());
                ret.push_back(v);
                e = true;
            }
        }
    }
};

662. 二叉树的最大宽度

662. 二叉树最大宽度 - 力扣(LeetCode)

宽度定义为“最左节点和最右节点之间的距离”,在这道题中,将题目给的二叉树视作满二叉树,两端点中间会出现一些延申到这一层的空节点,并且这些空节点也计入长度,下面来分析下这道题:

解法一: 

  • 解法一:硬来;由于空节点也要计入长度,如果遇到空节点,把空节点也加入到队列里,然后后面遇到空节点时,默认加两个空节点即可
  • 但是这样搞会有问题,以示例二为例,由于9只有左孩子一个孩子,那么右孩子会当成空加入到队列,但是这样的话队列的长度就是8了,不符合要求;这个也可以解决,就是用一个变量empty,统计空节点的个数,比如从6开始,empty一直++,直到遇到不是空节点也就是7时,empty + 2就是我们需要的长度。这种解法理论可行,但是提交过后会超时,假设3000个节点,然后只有2两条路径,就是纯左边和右边一路干到底,那么这个时间复杂度就非常夸张了,如下图:

解法二

  • 解法二:利用数组存储二叉树的方式给节点编号;树是可以通过顺序的方式存储的,比如堆结构,就是用的数组形式
  • 我们先回一下用数组存储二叉树:默认根节点的下标是从1开始计数,然后按照层次遍历的顺序依次遍历并且入数组。所以就可以发现一个规律,对于每个节点都有:“假设一个节点下标是x,那么它的左孩子下标是2x,右孩子下标是2x + 1”:
  • 所以我们仍然创建一个队列,但是队列不再存int了,而是存一个键值对pair,first表示这个节点的指针,second表示这个节点的编号,然后对于左右孩子的下标就按照上面的公式入队列
  • 然后就是计算宽度,直接拿出队头和队尾的元素,然后让两个的下标相减再+1就是我们最终需要的值
  • 细节一:如果再优化的话,我们可以直接用数组vector<pair<TreeNode*, int>>来代替队列,因为对于不同的语言对于队列的实现是不一样的,有的队列的容器可以查队头队尾,有的只能查队尾
  • 细节二:下标又可能溢出,以解法一的极端情况为例,左右如果各有1500个节点,那么数量就是2的1500次方-1,这个数是非常大的,任何数据类型都存不下;但是当我们相减之后,即使结果溢出,结果也是正确的,因为数据的存储可以看作是一个环形的,比如char类型,如下图:
  • 整型也是同理,就算溢出,相减完之后结果依旧是正确的,但是C++中用int的话会直接报错,所以我们用无符号整数unsigned int来存下标就OK了;如果没想到用unsigned int,那么就可以用字符串来存这个数,然后最后减法的时候做一次高精度减法也是可以的

 

class Solution 
{
    vector<pair<TreeNode*, unsigned int>> v;
    unsigned int ret;
public:
    int widthOfBinaryTree(TreeNode* root) 
    {
        bfs(root);
        return ret;
    }
    void bfs(TreeNode* root)
    {
        v.push_back({root, 1}); //先把根节点放进去
        while(!v.empty())
        {
            auto& [x1, y1] = v[0]; //拿到首元素
            auto& [x2, y2] = v.back(); //拿到结尾元素
            ret = max(ret, y2 - y1 + 1);

            //遍历下一层
            vector<pair<TreeNode*, unsigned int>> tmp; //让下一层进入这个队列,然后覆盖掉前面那个即可
            for(auto& [x, y] : v)
            {
                if(x->left) tmp.push_back({x->left, y * 2});
                if(x->right) tmp.push_back({x->right, y * 2 + 1});
            }
            v = tmp; //覆盖
        }
    }
};

515. 在每个树行中找最大值

515. 在每个树行中找最大值 - 力扣(LeetCode)

题目很简单,就算让我们找出一棵树中每一行的最大值然后返回即可:

  • 算法也很简单,就是在层序遍历的过程中,用一个变量记录下最大值,然后在进入下一次遍历之前记录一下最大值即可
class Solution 
{
    queue<TreeNode*> q;
    vector<int> ret;
public:
    vector<int> largestValues(TreeNode* root) 
    {
        bfs(root);
        return ret;
    }
    void bfs(TreeNode* root)
    {
        if(root == nullptr) return;
        q.push(root);
        while(!q.empty())
        {
            int tmp = INT_MIN;
            int sz = q.size();
            for(int i = 0; i < sz; i++)
            {
                auto t = q.front();
                q.pop();
                tmp = max(tmp, t->val);
                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
            ret.push_back(tmp);
        }
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/944623.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

cellphoneDB进行CCI以及可视化

除了cellchat&#xff0c;在单细胞转录组或者空间组的分析中&#xff0c;cellphoneDB也是一个常用的细胞通讯软件&#xff0c;这个数据库更注重配受体关系&#xff0c;对于有明确先验知识的配受体研究比较友好。 但值得注意的是&#xff0c;它的数据库只包括人的基因名称信息&…

003 字节码

字节码的位置 当我们讨论到字节码&#xff0c;我们需要清楚它在整个学习框架中的位置 如图&#xff0c;字节码是我们写的代码编译之后的结果&#xff0c;与虚拟机很近。 字节码是Java能实现跨平台的基础。 字节码基本知识体系 我们需要关注的点在于class文件的构成上。 字节…

基本算法——回归

本节将通过分析能源效率数据集&#xff08;Tsanas和Xifara&#xff0c;2012&#xff09;学习基本的回归算法。我们将基 于建筑的结构特点&#xff08;比如表面、墙体与屋顶面积、高度、紧凑度&#xff09;研究它们的加热与冷却负载要 求。研究者使用一个模拟器设计了12种不…

U盘文件剪切丢失的全方位解析与恢复指南

一、U盘文件剪切丢失现象描述 在日常使用U盘的过程中&#xff0c;我们时常会遇到需要将文件从一个位置移动到另一个位置的情况&#xff0c;而剪切加粘贴便是最常用的操作之一。然而&#xff0c;有时在剪切文件后&#xff0c;却意外发现目标位置并没有出现这些文件&#xff0c;…

洛谷 P1075 [NOIP2012 普及组] 质因数分解 C语言

题目&#xff1a; P1075 [NOIP2012 普及组] 质因数分解 - 洛谷 | 计算机科学教育新生态 题目描述 已知正整数 n 是两个不同的质数的乘积&#xff0c;试求出两者中较大的那个质数。 输入格式 输入一个正整数 n。 输出格式 输出一个正整数 p&#xff0c;即较大的那个质数。…

Lecture 17

10’s Complement Representation 主要内容&#xff1a; 1. 10’s 补码表示: • 10’s 补码表示法需要指定表示的数字位数&#xff08;用 n 表示&#xff09;。 • 表示的数字取决于 n 的位数&#xff0c;这会影响具体数值的解释。 2. 举例: • 如果采用 3 位补码&…

电子电器架构 --- 智能座舱HUD技术革新

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所谓鸡汤&#xff0c;要么蛊惑你认命&#xff0c;要么怂恿你拼命&#xff0c;但都是回避问题的根源&…

零基础微信小程序开发——全局配置之tabBar(保姆级教程+超详细)

&#x1f3a5; 作者简介&#xff1a; CSDN\阿里云\腾讯云\华为云开发社区优质创作者&#xff0c;专注分享大数据、Python、数据库、人工智能等领域的优质内容 &#x1f338;个人主页&#xff1a; 长风清留杨的博客 &#x1f343;形式准则&#xff1a; 无论成就大小&#xff0c;…

docker redis安装

一.镜像拉取 docker pull redis:5.0新建文件 touch /home/redis/redis.conf touch /home/redis/redis_6379.pid # bind 192.168.1.100 10.0.0.1 # bind 127.0.0.1 ::1 #bind 127.0.0.1protected-mode noport 6379tcp-backlog 511requirepass roottimeout 0tcp-keepali…

0基础跟德姆(dom)一起学AI 自然语言处理08-认识RNN模型

1 什么是RNN模型 RNN(Recurrent Neural Network), 中文称作循环神经网络, 它一般以序列数据为输入, 通过网络内部的结构设计有效捕捉序列之间的关系特征, 一般也是以序列形式进行输出. 一般单层神经网络结构: RNN单层网络结构: 以时间步对RNN进行展开后的单层网络结构: RNN的…

Xilinx PCIe高速接口入门实战(三)

引言&#xff1a;为保证FPGA设备可以连接并被系统识别&#xff0c;本节讨论了PCIe基础规范和PCIe板卡电气规范的对FPGA配置时间具体要求。 1. 配置访问时间 在PCIe的标准系统中&#xff0c;当系统通电时&#xff0c;处理器上运行的配置软件开始扫描PCIe总线以发现机器拓扑。…

InfoNCE Loss详解(上)

引言 InfoNCE对比学习损失是学习句嵌入绕不开的知识点&#xff0c;本文就从头开始来探讨一下它是怎么来的。 先验知识 数学期望与大数定律 期望(expectation&#xff0c;expected value&#xff0c;数学期望&#xff0c;mathematical expectation)是随机变量的平均值&#…

抽象工厂设计模式的理解和实践

在软件开发中&#xff0c;设计模式是前人通过大量实践总结出的、可复用的、解决特定问题的设计方案。它们为我们提供了一种标准化的解决方案&#xff0c;使得代码更加简洁、灵活和易于维护。在众多设计模式中&#xff0c;抽象工厂模式&#xff08;Abstract Factory Pattern&…

爱思唯尔word模板

爱思唯尔word模板 有时候并不一定非得latex https://download.csdn.net/download/qq_38998213/90199214 参考文献书签链接

【机器学习】工业 4.0 下机器学习如何驱动智能制造升级

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 随着科技的飞速发展&#xff0c;工业 4.0 浪潮正席卷全球制造业&#xff0c;而机器学习作为这一变革中的关键技术&#xff0c;正以前…

全面了解 SQL Server:功能、优势与最佳实践

SQL Server 是微软公司推出的一款关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛应用于企业级数据存储、数据分析、应用开发等领域。作为全球最受欢迎的数据库管理系统之一&#xff0c;SQL Server 提供了强大的功能和工具&#xff0c;支持从小型应用到大型…

旅游管理系统|Java|SSM|VUE| 前后端分离

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库可…

攻防世界 robots

开启场景 根据提示访问/robots.txt&#xff0c;发现了 f1ag_1s_h3re.php 拼接访问 /f1ag_1s_h3re.php 发现了 flag cyberpeace{d8b7025ed93ed79d44f64e94f2527a17}

离线语音识别+青云客语音机器人(幼儿园级别教程)

1、使用步骤 确保已安装以下库&#xff1a; pip install vosk sounddevice requests pyttsx3 2、下载 Vosk 模型&#xff1a; 下载适合的中文模型&#xff0c;如 vosk-model-small-cn-0.22。 下载地址&#xff1a; https://alphacephei.com/vosk/models 将模型解压后放置在…

秒杀场景的设计思考

秒杀场景的设计思考 在学习Redis的之后&#xff0c;一个绕不开的话题就是秒杀系统的设计。本文将从下面&#x1f447;&#x1f3fb;几个方面展开一下个人简单的理解&#xff1a; 秒杀场景的介绍设计的核心思路怎么限流、削峰、异步planB总结 ‍ 秒杀场景的介绍 秒杀场景是…