算法沉淀——队列+宽度优先搜索(BFS)(leetcode真题剖析)

news2024/11/26 4:30:47

在这里插入图片描述

算法沉淀——队列+宽度优先搜索(BFS)

  • 01.N 叉树的层序遍历
  • 02.二叉树的锯齿形层序遍历
  • 03.二叉树最大宽度
  • 04.在每个树行中找最大值

队列 + 宽度优先搜索算法(Queue + BFS)是一种常用于图的遍历的算法,特别适用于求解最短路径或最少步数等问题。该算法通常用于在图中寻找从起点到目标点的最短路径。

基本思想:

  1. 初始化队列: 将起始节点放入队列中。
  2. BFS遍历: 从队列中取出一个节点,遍历与该节点相邻且未访问过的节点,将其加入队列。
  3. 标记已访问: 标记已访问的节点,避免重复访问。
  4. 重复步骤2和3: 直到队列为空。

这个算法适用于无权图的最短路径问题。在搜索的过程中,每一层级的节点都会被依次访问,直到找到目标节点。

具体步骤:

  1. 将起始节点加入队列。
  2. 进行循环直到队列为空: a. 从队列中取出一个节点。 b. 如果该节点是目标节点,返回结果。 c. 否则,将与该节点相邻且未访问过的节点加入队列,并标记为已访问。

这种算法适用于许多场景,例如迷宫问题、游戏中的寻路问题、网络路由算法、树问题等。在这些问题中,它能够有效地找到最短路径或最优解。

01.N 叉树的层序遍历

题目链接:https://leetcode.cn/problems/n-ary-tree-level-order-traversal/

给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

示例 1:
在这里插入图片描述

输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]

示例 2:

在这里插入图片描述

输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]] 

提示:

  • 树的高度不会超过 1000
  • 树的节点总数在 [0, 10^4] 之间

思路

在树的层序遍历中经常要使用到的就是队列和宽度优先搜索算法,这是一道经典的队列和宽度优先搜索算法模板题

  1. 初始化一个空的二维向量 ret 用于存储层次遍历的结果。

  2. 如果根节点 root 为空,直接返回空向量 ret

  3. 创建一个队列 q 并将根节点入队。

  4. 进入主循环,该循环将处理每一层的节点: a. 获取当前队列的大小,即当前层的节点数。 b. 创建一个临时向量 tmp

    用于存储当前层的节点值。 c. 对于当前层的每个节点:

    • 出队一个节点 t
    • 将节点值 t->val 存入 tmp
    • 将该节点的所有子节点入队,如果子节点非空。 d. 将 tmp 存入 ret
  5. 返回最终的层次遍历结果 ret

代码

/*
// 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) return ret;
        q.push(root);
        while(q.size()){
            int n=q.size();
            vector<int> tmp;
            for(int i=0;i<n;++i){
                Node* t=q.front();
                tmp.push_back(t->val);
                for(Node* x:t->children) if(x) q.push(x);
                q.pop();
            }
            ret.push_back(tmp);
        }
        return ret;
    }
};

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

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

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

示例 1:

在这里插入图片描述

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[] 

提示:

  • 树中节点数目在范围 [0, 2000]
  • -100 <= Node.val <= 100

思路

这一题我们仔细理解题意,我们不难发现这题和上一题的区别就是,在偶数行时需要逆序,所以我们只要再添加一个偶数列逆序的操作即可,其余同上。

  1. 引入一个标志变量 flag,用于标识当前层次是奇数层还是偶数层。初始化为0。
  2. 初始化一个队列 q 用于层次遍历,以及一个二维向量 ret 用于存储结果。
  3. 如果根节点 root 为空,直接返回空向量 ret
  4. 将根节点入队。
  5. 进入主循环,该循环处理每一层的节点: a. 获取当前队列的大小,即当前层的节点数,用 s 表示。 b. 递增 flag。 c. 创建一个临时向量 tmp 用于存储当前层的节点值。 d. 对于当前层的每个节点:
    • 出队一个节点 t
    • 将节点值 t->val 存入 tmp
    • 如果节点 t 的左子节点非空,将其入队。
    • 如果节点 t 的右子节点非空,将其入队。 e. 如果 flag 为偶数,反转 tmp 中的元素顺序。 f. 将 tmp 存入 ret
  6. 返回最终的层次遍历结果 ret

代码

/**
 * 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) {
        queue<TreeNode*> q;
        vector<vector<int>> ret;
        if(!root) return ret;
        q.push(root);
        int flag=0;
        while(q.size()){
            int s=q.size();
            flag++;
            vector<int> tmp;
            for(int i=0;i<s;++i){
                TreeNode* t=q.front();
                tmp.push_back(t->val);
                q.pop();
                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
            if(flag%2==0) reverse(tmp.begin(),tmp.end());
            ret.push_back(tmp);
        }
        return ret;
    }
};

03.二叉树最大宽度

题目链接:https://leetcode.cn/problems/maximum-width-of-binary-tree

给你一棵二叉树的根节点 root ,返回树的 最大宽度

树的 最大宽度 是所有层中最大的 宽度

每一层的 宽度 被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null 节点,这些 null 节点也计入长度。

题目数据保证答案将会在 32 位 带符号整数范围内。

示例 1:
在这里插入图片描述

输入:root = [1,3,2,5,3,null,9]
输出:4
解释:最大宽度出现在树的第 3 层,宽度为 4 (5,3,null,9) 。

示例 2:
在这里插入图片描述

输入:root = [1,3,2,5,null,null,9,6,null,7]
输出:7
解释:最大宽度出现在树的第 4 层,宽度为 7 (6,null,null,null,null,null,7) 。

示例 3:
在这里插入图片描述

输入:root = [1,3,2,5]
输出:2
解释:最大宽度出现在树的第 2 层,宽度为 2 (3,2) 。

提示:

  • 树中节点的数目范围是 [1, 3000]
  • -100 <= Node.val <= 100

思路

这道题最大的坑点在于如果二叉树极度不平衡,若使用模拟的方式,空节点也进行插入操作去计算的话,空间是远远不够的,所以这里我们不能像前面两题这样操作,我们可以通过计算每层插入节点的头和尾下标差值,并使用vector来模拟队列操作,每次都覆盖前一层,以防超出内存,还有计算差值,我们使用无符号整型,这样我们可以避免数据溢出带来的计算错误的值

  1. 定义一个队列 q,其中每个元素是一个 pair,包含一个二叉树节点指针和该节点在完全二叉树中的编号。
  2. 将根节点和其对应编号 1 放入队列 q 中。
  3. 初始化一个变量 ret 用于存储最大宽度。
  4. 进入主循环,该循环用于遍历二叉树的每一层。 a. 获取当前队列的首尾元素,即队列中最左边和最右边的节点及其编号。 b. 计算当前层的宽度,即 y2 - y1 + 1,其中 y1 是最左边节点的编号,y2 是最右边节点的编号。 c. 更新 ret,取 ret 和当前层宽度的较大值。 d. 创建一个临时队列 tmp。 e. 遍历队列 q 中的每个节点:
    • 如果节点有左子节点,将左子节点及其编号(编号乘以 2)加入 tmp
    • 如果节点有右子节点,将右子节点及其编号(编号乘以 2 加 1)加入 tmp。 f. 将 tmp 赋值给队列 q
  5. 返回最终的宽度 ret

代码

/**
 * 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;
    }
};

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

题目链接:https://leetcode.cn/problems/find-largest-value-in-each-tree-row/

给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。

示例1:
在这里插入图片描述

输入: root = [1,3,2,5,3,null,9]
输出: [1,3,9]

示例2:

输入: root = [1,2,3]
输出: [1,3]

提示:

  • 二叉树的节点个数的范围是 [0,104]
  • -231 <= Node.val <= 231 - 1

思路

根据前面几个题型,我们不难想到,无非就是在层序遍历的基础上增加一个每层的最大值计算,在之前的基础上增加条件即可。

  1. 定义一个队列 q,其中每个元素是二叉树节点指针。
  2. 将根节点放入队列 q 中。
  3. 初始化一个空的数组 ret,用于存储每一层的最大值。
  4. 进入主循环,该循环用于遍历二叉树的每一层。 a. 初始化一个变量 mINT_MIN,用于记录当前层的最大值。 b. 获取当前队列的大小(即当前层的节点数)。 c. 遍历当前层的每个节点:
    • 弹出队列的首元素,即最左边的节点。
    • 更新 m,取 m 和当前节点值的较大值。
    • 如果节点有左子节点,将左子节点加入队列 q
    • 如果节点有右子节点,将右子节点加入队列 q。 d. 将 m 添加到数组 ret 中。
  5. 返回最终的数组 ret,其中包含了每一层的最大值。

代码

/**
 * 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) {
        queue<TreeNode*> q;
        vector<int> ret;
        if(!root) return ret;
        q.push(root);

        while(q.size()){
            int m=INT_MIN;
            int n=q.size();
            for(int i=0;i<n;++i){
                auto t=q.front();
                q.pop();
                m=max(m,t->val);

                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
            ret.push_back(m);
        }
        return ret;
    }
};

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

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

相关文章

【python学习篇1】python基本语法

目录 一、第一个python程序 二、基本语法&#xff0c;数据类型&#xff0c;字面量&#xff0c;循环语句等内容 2.1字面量 2.2注释 2.2.1单行注释 2.2.2多行注释 2.3变量 2.3.1认识变量 2.3.2查看数据类型 2.3.3数据类型转换 2.3.4字符串的三种定义方式 2.3.5字符串…

Unicode编码的魅力:跨语言交流的桥梁

title: Unicode编码的魅力&#xff1a;跨语言交流的桥梁 date: 2024/2/15 14:04:00 updated: 2024/2/15 14:04:00 tags: Unicode编码跨语言多语言支持存储开销兼容性文本处理全球化软件 引言&#xff1a; Unicode编码是一种用于表示世界上所有字符的标准编码方式。它解决了字…

【机器学习】合成少数过采样技术 (SMOTE)处理不平衡数据(附代码)

1、简介 不平衡数据集是机器学习和人工智能中普遍存在的挑战。当一个类别中的样本数量明显超过另一类别时&#xff0c;机器学习模型往往会偏向大多数类别&#xff0c;从而导致性能不佳。 合成少数过采样技术 (SMOTE) 已成为解决数据不平衡问题的强大且广泛采用的解决方案。 …

mysql5.6安装---windows版本

安装包下载 链接&#xff1a;https://pan.baidu.com/s/1L4ONMw-40HhAeWrE6kluXQ 提取码&#xff1a;977q 安装视频 1.解压完成之后将其放到你喜欢的地址当中去&#xff0c;这里我默认放在了D盘&#xff0c;这是我的根目录 2.配置环境变量 我的电脑->属性->高级->环境…

基础链表代码实现

我们以题目为切入点&#xff0c;深入了解链表代码实现。 题目&#xff08;单项链表&#xff09; 题目描述 实现一个数据结构&#xff0c;维护一张表&#xff08;最初只有一个元素 1&#xff09;。需要支持下面的操作&#xff0c;其中 x 和 y 都是 1 到 1000000 范围内的正整…

Java 基于 SpringBoot+Vue 的智慧外贸平台的研究与实现,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

vuex中mutations详解,与actions的区别

Vuex 的 Mutations 是用于改变 Vuex Store 中状态的一种方式。它是一个同步的操作&#xff0c;用于直接修改 Store 中的状态。 Mutations 有以下特点&#xff1a; 同步操作&#xff1a;Mutations 是同步的&#xff0c;这意味着它们会立即执行并修改状态。原子性&#xff1a;…

数据库从入门到精通(一)数据库基础操作

mysql数据库基础操作 cmd下启动mysql数据库操作命令数据库重要的删除操作数据库增删改查操作插入数据更新数据删除数据查询数据查询指定记录in查询满足指定范围之内的条件记录not in查询不在指定范围之内的条件记录带between and 的范围查询带like的字符匹配查询(d%以d开头,%d以…

猫头虎分享:2024年值得程序员关注的技术发展动向分析

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

Hadoop:认识MapReduce

MapReduce是一个用于处理大数据集的编程模型和算法框架。其优势在于能够处理大量的数据&#xff0c;通过并行化来加速计算过程。它适用于那些可以分解为多个独立子任务的计算密集型作业&#xff0c;如文本处理、数据分析和大规模数据集的聚合等。然而&#xff0c;MapReduce也有…

cron表达式介绍和使用

Cron表达式是一种用于配置定时任务的字符串&#xff0c;它由数字、字符和符号组成&#xff0c;用于指定任务在某个时间点或周期性地执行。其通常包含六个或七个字段&#xff0c;每个字段代表一个时间单位&#xff0c;如下表所示&#xff1a; 域必须取值范围特殊字符秒是[0, 59…

OpenGL-ES 学习(2)---- DepthTest

深度测试 OpenGL-ES 深度测试是指在片段着色器执行之后&#xff0c;利用深度缓冲区所保存的深度值决定当前片段是否被丢弃的过程 深度缓冲区通常和颜色缓冲区有着相同的宽度和高度&#xff0c;一般由窗口系统自动创建并将其深度值存储为 16、 24 或 32 位浮点数。(注意只保存…

EasyRecovery2024全新官方汉化中文版下载

确实&#xff0c;EasyRecovery以其强大的功能而闻名。以下是它的一些主要功能特点&#xff1a; 全面恢复能力&#xff1a;EasyRecovery可以恢复从各种存储设备中丢失的数据&#xff0c;包括硬盘、U盘、SD卡、数码相机、手机等。无论是因为误删除、格式化、分区丢失、病毒攻击还…

2-7基础算法-位运算

一.基础 位运算经常考察异或的性质、状态压缩、与位运算有关的特殊数据结构、构造题。 位运算只能应用于整数&#xff0c;且一般为非负整数&#xff0c;不能应用于字符、浮点等类型。 左移操作相当于对原数进行乘以2的幂次方的操作&#xff0c;低位补0 右移操作相当于对原数进…

Elasticsearch使用场景深入详解

Elasticsearch是一个开源的、分布式的、RESTful风格的搜索和数据分析引擎。它能够解决越来越多的用例&#xff0c;并不仅仅局限于全文搜索。以下是Elasticsearch的一些主要使用场景及其深入详解。 1. 全文搜索 Elasticsearch最初和最基本的应用场景就是全文搜索。全文搜索是指…

上位机图像处理和嵌入式模块部署(上位机主要功能)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前关于机器视觉方面&#xff0c;相关的软件很多。比如说商业化的halcon、vision pro、vision master&#xff0c;当然也可以用opencv、pytorch自…

第18讲 投票帖子管理实现

后端&#xff1a; /*** 删除指定id的投票信息* param id* return*/ GetMapping("/delete/{id}") Transactional public R delete(PathVariable(value "id")Integer id){voteDetailService.remove(new QueryWrapper<VoteDetail>().eq("vote_id…

clang前端

Clang可以处理C、C和Objective-C源代码 Clang简介 Clang可能指三种不同的实体&#xff1a; 前端&#xff08;在Clang库中实现&#xff09;编译驱动程序&#xff08;在clang命令和Clang驱动程序库中实现&#xff09;实际的编译器&#xff08;在clang-ccl命令中实现&#xff0…

Codeforces Round 925 (Div. 3) E. Anna and the Valentine‘s Day Gift (Java)

Codeforces Round 925 (Div. 3) E. Anna and the Valentine’s Day Gift (Java) 比赛链接&#xff1a;Codeforces Round 925 (Div. 3) E题传送门&#xff1a;E. Anna and the Valentine’s Day Gift 题目&#xff1a;E. Anna and the Valentine’s Day Gift 样例 #1 样例输…

DS:二叉树的顺序结构及堆的实现

创作不易&#xff0c;兄弟们给个三连&#xff01;&#xff01; 一、二叉树的顺序存储 顺序结构指的是利用数组来存储&#xff0c;一般只适用于表示完全二叉树&#xff0c;原因如上图&#xff0c;存储不完全二叉树会造成空间上的浪费&#xff0c;有的人又会问&#xff0c;为什么…