【算法】队列与BFS

news2024/9/21 4:29:03

【ps】本篇有 4 道 leetcode OJ。

目录

一、算法简介

二、相关例题

1)N 叉树的层序遍历

.1- 题目解析

.2- 代码编写

2)二叉树的锯齿形层序遍历

.1- 题目解析

.2- 代码编写

3)二叉树最大宽度

.1- 题目解析

.2- 代码编写

4)在每个树行中找最大值

.1- 题目解析

.2- 代码编写


一、算法简介

        和栈类似,队列也是一种特殊的线性数据结构,是只允许在一端进行插入数据操作、在另一端进行删除数据操作的特殊线性表,具有先进先出的特性。进行插入操作(入队)的一端称为队尾,进行删除操作(出队)的一端称为队头

        队列一般与 BFS(宽度优先搜索)结合出题,而 BFS 主要的题型有树形结构相关(多为二叉树)、图论相关(最短路问题、迷宫问题、拓扑排序等)。

二、相关例题

1)N 叉树的层序遍历

429. N 叉树的层序遍历

.1- 题目解析

        在对多叉树进行层序遍历的时候,是每一层从左向右依此遍历,然后再进入下一层继续从左向右依此遍历的,而遍历完一层要进入下一层时,又要回到当前层的第一个节点,通过它的孩子节点,这样才能进入下一层继续遍历,而这个过程其实是符合队列先进先出的特点的,因此队列特别适合于这类题型。

        我们创建一个队列来模拟层序遍历的过程,且用一个变量来记录队列中元素的个数。若根节点不为空,就将根节点入队,此时队列中只有一个元素,仅需出队一次就能得到当前层的结果;然后将根节点的孩子节点全部入队,再记录当前队列中元素的个数,相应的,仅需出队元素的个数次就能得到当前层的结果......因此类推,直至队列中没有元素,就完成了多叉树的层序遍历。

.2- 代码编写

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> ret; //记录最终结果
        queue<Node*> q;          //用队列模拟层序遍历的过程
        if(root==nullptr)
            return ret;
        q.push(root);    //根节点入队
        while(q.size())
        {
            int sz=q.size(); //记录当前队列中的元素个数(树当前层元素的个数)
            vector<int> tmp; //统计当前层的节点
            for(int i=0;i<sz;i++)
            {
                Node* t=q.front();//取队头
                q.pop();
                tmp.push_back(t->val);
                for(Node* child:t->children) //让孩子节点入队
                {
                    if(child) q.push(child);
                }
            }
            ret.push_back(tmp); //统计最终结果
        }
        return ret;
    }
};

2)二叉树的锯齿形层序遍历

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

.1- 题目解析

        在正常的层序遍历过程中,我们是可以把一层的节点放在一个数组中去的。 既然我们有这个数组,在合适的层数逆序就可以得到锯齿形层序遍历的结果。由此,我们可以弄一个计数器 level 来记录当前的层数,当 level 为偶数时,就对数组进行逆序,再整体插入结果中。其余过程与上道题几乎相同。

.2- 代码编写

/**
 * 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>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ret;
        if(root==nullptr)return ret;
        queue<TreeNode*> q;
        q.push(root);
        int level=1; //记录当前的层数
        while(q.size())
        {
            int sz=q.size();
            vector<int> tmp;
            for(int i=0;i<sz;i++)
            {
                auto t=q.front();
                q.pop();
                tmp.push_back(t->val);
                if(t->left)q.push(t->left);
                if(t->right)q.push(t->right);
            }
            if(level%2==0) //偶数层则需进行逆序
                reverse(tmp.begin(),tmp.end());
            ret.push_back(tmp);
            level++;
        }
        return ret;
    }
};

3)二叉树最大宽度

662. 二叉树最大宽度

.1- 题目解析

        在计算二叉树的最大宽度时,还要计入空节点。

        对于统计每一层的最大宽度,不难想到利用层序遍历,把当前层的结点全部存在队列里面,利用队列的长度来计算每一层的宽度,统计出最大的宽度。 此外,由于空节点也是需要计算在内的,因此空节点也需要存在队列里面。 但极端境况下,树的最左边一条长链,最右边也是一条长链,就需要存若干个空节点,这样就会超过最大内存限制。

        不过,我们还可以想到堆,借鉴在堆中寻找根的左右孩子的方式来解题。堆的逻辑结构是一棵树,而物理结构一个顺序结构,要通过一个根节点去寻找它的左右孩子,可以根据公式 “2 * 根节点下标 + 1” 或 “2 * 根节点下标 + 2”,在顺序结构中找到左右孩子的下标。

        那么,我们也可以仿照堆利用数组来存储二叉树的方式,给树节点都编上号,那么树中的空节点就可以忽略不计了,最终只需取最宽一层的最左边的节点和最右边的节点,两者的下标相减再 + 1 即可求得二叉树的最大宽度。

        对一颗满二叉树进行编号,我们不难发现,如果编号是从 1 开始的,假设一个根节点的编号为 x,那么这个根节点的左孩子的编号为 2x,右孩子的编号为 2x + 1。

        特别的,我们用 pair 类型将节点和其对应的编号一起存入数组中,以方便后续找到每一层的最左边节点和最右边节点。当一个节点进入数组时,只需看看它是其根节点的左孩子还是右孩子,然后根据左右孩子的编号的公式去求得它的编号即可。

        但由于有效节点和空节点的个数可能很多,下标是可能溢出的,好在数据的存储是一个环形的结构,且题目说明了,数据的范围在 int 这个类型的范围之内,因此最终求下标的差值,就无需考虑溢出的情况,只需用 unsigned int 作为下标和结果的变量类型即可。

.2- 代码编写

/**
 * 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 widthOfBinaryTree(TreeNode* root) {
        vector<pair<TreeNode*, unsigned int>> q; // ⽤数组模拟队列
        q.push_back({root, 1});
        unsigned int ret = 0;
        while (q.size()) {
            // 先更新这⼀层的宽度
            auto& [x1, y1] = q[0];
            auto& [x2, y2] = q.back();
            ret = max(ret, y2 - y1 + 1);
            // 让下⼀层进队
            vector<pair<TreeNode*, unsigned int>> tmp; // 让下⼀层进⼊这个队列
            for (auto& [x, y] : q)
            {
                if (x->left)
                    tmp.push_back({x->left, y * 2});
                if (x->right)
                    tmp.push_back({x->right, y * 2 + 1});
            }
            q = tmp;
        }
        return ret;
    }
};

4)在每个树行中找最大值

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

.1- 题目解析

        在用队列模拟层序遍历的时候,顺便统计每一层的最大值即可。

.2- 代码编写

/**
 * 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<int> largestValues(TreeNode* root) {
        vector<int> ret;
        if(root==nullptr)return ret;
        queue<TreeNode*> q;
        q.push(root);
        while(q.size())
        {
            int sz=q.size();
            int tmp=INT_MIN;
            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);
        }
        return ret;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UART协议

目录 一、概述二、帧格式起始位数据位奇偶校验位停止位 三、数据传输过程四、串行通信接口RS232RS422RS485 五、UART环回程序设计 参考&#xff1a;正点原子FPGA开发指南 一、概述 UART&#xff08;通用异步收发器&#xff09;是一种异步、全双工的串行通信总线&#xff0c;在…

CGAL 从DSM到DTM holefilling remeshing

CGAL 从DSM到DTM holefilling & remeshing 上一节简单地删除被建筑物覆盖的大片区域中的顶点会导致大的Delaunay三角面&#xff0c;从而得到了较差的DTM&#xff0c;所以一个额外的步骤可以帮助产生更好的形状网格:删除大于阈值的面&#xff0c;并用孔洞填充算法进行三角化…

C++速通LeetCode简单第17题-爬楼梯

思路要点&#xff1a;将问题转化为求斐波那契数列的第n项&#xff0c;然后迭代。 思路分析&#xff1a;最后一次爬的阶数不是1就是2&#xff0c;假设爬n阶的方法数是f(n)&#xff0c;假设最后一次爬1阶&#xff0c;那么爬前面的 n-1阶的方法数是f(n-1)&#xff1b;假设最后一次…

力扣题解2848

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述&#xff08;简单&#xff09;&#xff1a; 与车相交的点 给你一个下标从 0 开始的二维整数数组 nums 表示汽车停放在数轴上的坐标。对于任意下标 i&#xff0c;nums[i] [starti, endi] &…

熵权法详细讲解+Python代码实现

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

大数据新视界 --大数据大厂之 Cassandra 分布式数据库:高可用数据存储的新选择

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

基于单片机的超声波液位检测系统(论文+源码)

1总体设计 本课题为基于单片机的超声波液位检测系统的设计&#xff0c;系统的结构框图如图2.1所示。其中包括了按键模块&#xff0c;温度检测模块&#xff0c;超声波液位检测模块&#xff0c;显示模块&#xff0c;蜂鸣器等器件设备。其中&#xff0c;采用STC89C52单片机作为主…

2.5 Trigger源码分析 -- ant-design-vue系列

Trigger源码分析 – ant-design-vue系列 1 概述 源码地址&#xff1a; https://github.com/vueComponent/ant-design-vue/blob/main/components/vc-trigger/Trigger.tsx 在源码的实现中&#xff0c;Trigger组件主要有两个作用&#xff1a; 使用Portal组件&#xff0c;把Pop…

构建响应式 Web 应用:Vue.js 基础指南

构建响应式 Web 应用&#xff1a;Vue.js 基础指南 一 . Vue 的介绍1.1 介绍1.2 好处1.3 特点 二 . Vue 的快速入门2.1 案例 1 : 快速搭建 Vue 的运行环境 , 在 div 视图中获取 Vue 中的数据2.2 案例 2 : 点击按钮执行 vue 中的函数输出 vue 中 data 的数据2.3 小结 三 . Vue 常…

Leetcode3282. 到达数组末尾的最大得分

Every day a Leetcode 题目来源&#xff1a;3282. 到达数组末尾的最大得分 解法1&#xff1a;动态规划 代码&#xff1a; class Solution { public:long long findMaximumScore(vector<int>& nums) {if (nums.size() < 1) return 0LL;int n nums.size();vect…

JavaScript高级——循环遍历加监听

本文分享到这里&#xff0c;欢迎大家评论区相互讨论学习&#xff0c;下一篇继续分享JavaScript高级学习中的闭包的内容。

linux进程间通信——学习与应用命名管道, 日志程序的使用与实现

前言&#xff1a;本节主要讲解linux进程间通信里面的命名管道&#xff0c; 命名管道和我们学过的匿名管道是类似的。 博主将会带着友友们先看一下原理&#xff0c; 然后就会着手使用以下命名管道是怎么使用的。 最后我们还会试着引入日志系统&#xff0c; 我们从本节开始就会引…

npm 安装 与 切换 淘宝镜像

一、镜像源 npm默认镜像源是国外的&#xff0c;安装依赖速度较慢&#xff0c;使用国内的镜像源速度会快一些。 1、设置淘宝镜像源&#xff1a; #最新地址 淘宝 NPM 镜像站喊你切换新域名啦! npm config set registry https://registry.npm.taobao.org&#xff08;弃用了&…

网站采用H5+CSS3开发的优势和劣势

在现代网站开发中&#xff0c;HTML5和CSS3的结合使用已经成为一种趋势。以下是其优势和劣势的介绍&#xff1a; 优势 增强的多媒体支持&#xff1a;HTML5引入了新的标签&#xff0c;使开发者能够轻松嵌入音频、视频和图形&#xff0c;无需依赖第三方插件如Flash。这大大简化了…

【AI大模型】ChatGPT模型原理介绍(下)

目录 &#x1f354; GPT-3介绍 1.1 GPT-3模型架构 1.2 GPT-3训练核心思想 1.3 GPT-3数据集 1.4 GPT-3模型的特点 1.5 GPT-3模型总结 &#x1f354; ChatGPT介绍 2.1 ChatGPT原理 2.2 什么是强化学习 2.3 ChatGPT强化学习步骤 2.4 监督调优模型 2.5 训练奖励模型 2.…

基于单片机的风机故障检测装置的设计与实现(论文+源码)

1 系统总体设计方案 通过对风机故障检测装置的设计与实现的需求、可行性进行分析&#xff0c;本设计风机故障检测装置的设计与实现的系统总体架构设计如图2-1所示&#xff0c;系统风机故障检测装置采用STM32F103单片机作为控制器&#xff0c;并通过DS18B20温度传感器、ACS712电…

macOS使用brew安装并配置python环境

1.确认已安装brew环境,如没有安装,参考: macOS系统Homebrew工具安装及使用-CSDN博客 2.安装python python安装成功 3.添加pip路径到/etc/paths 4.查看python与pip默认安装版本

【leetcode】树形结构习题

二叉树的前序遍历 返回结果&#xff1a;[‘1’, ‘2’, ‘4’, ‘5’, ‘3’, ‘6’, ‘7’] 144.二叉树的前序遍历 - 迭代算法 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,…

git 更换远程地址的方法

需要将正在开发的代码远程地址改成新的地址&#xff0c;通过查询发现有三个方法可以实现&#xff0c;特此记录。具体方法如下&#xff1a; &#xff08;1&#xff09;通过命令直接修改远程仓库地址 git remote 查看所有远程仓库git remote xxx 查看指定远程仓库地址git remote…

外卖会员卡是不是一个骗局?

大家好&#xff0c;我是鲸天科技千千&#xff0c;大家都知道我是做小程序开发的&#xff0c;平时会给大家分享一些互联网相关的创业项目&#xff0c;感兴趣的可以跟我关注一下。 首先就是要搭建一个自己的外卖会员卡系统小程序&#xff0c;我们自己的工作就是把这个小程序推广…