代码随想录DAY17 - 二叉树 - 08/16

news2024/11/14 3:55:56

最大二叉树

题干

题目:给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  • 创建一个根节点,其值为 nums 中的最大值。

  • 递归地在最大值 左边 的 子数组前缀上 构建左子树。

  • 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 。

注意:nums 中的所有整数 互不相同。

链接:. - 力扣(LeetCode)

思路和代码

给定的数组其实就是中序序列,其中最大值为根节点,每次都以最大值为分界划分左右子树。这与之前做的一道题 “利用中序序列和前序序列构造二叉树” 有异曲同工之妙,之前我们是根据前序序列的 “中左右” 顺序来确定根节点就是前序序列的第一个元素。而在这道题中,是直接告诉了我们根节点为最大值元素。

递归法

按照前序遍历的顺序,先找出数组中的最大值作为根节点,再划分为左右子树序列,递归构建左右子树即可。

class Solution {
public:
    // 找最大值下标
    int findMax(vector<int> &nums){
        int max = nums[0];
        int index = 0;
        for (int i = 0; i < nums.size(); ++i) {
            if (max < nums[i]){
                max = nums[i];
                index = i; // 更新最大值下标
            }
        }
        return index;
    }
    // 构建二叉树
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        if (nums.size() == 0) return nullptr;
        int maxIndex = findMax(nums); // 找序列中的最大值下标
        TreeNode* root = new TreeNode(nums[maxIndex]); // 创建根节点
        // 左子树序列
        vector<int> leftTree(nums.begin(),nums.begin()+maxIndex);
        // 右子树序列
        vector<int> rightTree(nums.begin()+maxIndex+1,nums.end());
        // 创建左子树
        TreeNode* leftNode = constructMaximumBinaryTree(leftTree);
        // 创建右子树
        TreeNode* rightNode = constructMaximumBinaryTree(rightTree);
        root->left = leftNode;
        root->right = rightNode;
        return root;
    }
};
递归法:优化

和昨天的题目类似,我们在递归建立左右子树时需要知道左右子树的序列,但是我们并不一定需要新建数组存储子树序列,只要能知道子树序列对应的索引下标范围即可。

  • 递归参数和返回值:参数是原始序列数组、传入的子树的序列起始下标和终止下标;返回值是当前树的根节点。

  • 递归的结束条件:当起始下标比终止下标要大或相等,说明此时传进来的是空结点,直接返回空指针。

  • 递归的顺序:根据前序遍历 “中左右” 的顺序,先建立根结点,再递归建立左右子树。

注意!!!!序列数组 nums 在传参时一定要取地址!!

class Solution {
public:
    // 左闭右开
    TreeNode* BuildTree(vector<int> &nums, int start, int end) {
        // 这里的参数 nums 虽然取不取地址都可以,因为 nums 并没有在递归中被改变。
        // 但是如果取地址,传进来的就是指针型变量,不需要新建数组
        // 如果不取nums地址,则传参时会在栈空间中新建vector数组赋值,会很耗时!!
        
        // 空结点
        if (start >= end){
            return nullptr;
        }
        // 找最大值下标
        int maxValue = nums[start]; // 记录最大值
        int maxIndex = start; // 最大值下标
        for (int i = start+1; i < end; ++i) {
            if (nums[i] > maxValue){
                maxValue = nums[i];
                maxIndex = i;
            }
        }
        // 建立根节点
        TreeNode* root = new TreeNode(nums[maxIndex]);
        // 左子树序列
        int leftStart = start;
        int leftEnd = maxIndex;
        // 右子树序列
        int rightStart = maxIndex+1;
        int rightEnd = end;
        // 递归建立左右子树
        root->left = BuildTree(nums,leftStart,leftEnd);
        root->right = BuildTree(nums,rightStart,rightEnd);
        return root;
    }
    TreeNode* constructMaximumBinaryTree(vector<int>& nums){
        return BuildTree(nums,0,nums.size());
    }
};

合并二叉树

题干

题目:给你两棵二叉树 root1 和 root2 。想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。返回合并后的二叉树。

注意:合并过程必须从两个树的根节点开始。

链接:. - 力扣(LeetCode)

思路和代码

递归法

题目需要返回合并后的二叉树,那么可以重复利用 root1 来存储合并后的二叉树,只需让 root1 和 root2 对比,不需要新建树。

  • 递归参数和返回值:参数分别是两个树的根,返回值是合并后的树的根结点。

  • 递归结束的条件:当两个树的根都为空,则返回空指针;若根1非空,根2空,返回根1;若根1空,根2非空,返回根2;

  • 递归顺序:根据前序遍历 “中左右” 的顺序,先看两个根节点是否重叠,若重叠则相加,根节点处理完再先后合并左右子树。

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        // 两棵树均为空
        if (!root1 && !root2) return nullptr;
        // root1 非空,root2 空,合并后应该是返回 root1
        if (root1 && !root2) return root1;
        // root1 空,root2 非空,合并后应该是返回 root2
        if (!root1 && root2) return root2;
        // 前序遍历
        root1->val += root2->val;
        root1->left = mergeTrees(root1->left,root2->left);
        root1->right = mergeTrees(root1->right,root2->right);
        return root1;
    }
};
迭代法:层序遍历

同时层序遍历两棵树 root1 和 root2,用一个队列 queue 同时存储两棵树对应的结点,再相互比较这两个结点。同样还是利用 root1 存储合并后的二叉树。

在层序遍历的过程中,我们需要先判断当前两个树的结点是否都有左右孩子(即是否重叠),如果有,则让左右孩子相应入队;执行完上一步判断,才执行 root1 孩子为空而 root2 孩子非空的情况,因为我们是用 root1 存储合并后的树,root1 缺少的需要 root2 进行补齐,而 root2 缺少的不需要理会。这两个步骤的顺序不可以颠倒!!

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        // 两颗树中只要有一颗为空,返回非空的那棵
        if (!root1) return root2;
        if (!root2) return root1;
        // 层序遍历
        queue<TreeNode*> layer; // 存储一层的结点
        layer.push(root1);
        layer.push(root2);
        int count = 1;
        while (!layer.empty()){
            TreeNode* cur1 = layer.front();
            layer.pop();
            TreeNode* cur2 = layer.front();
            layer.pop();
            // 我们要保证传入的两个结点都非空
            cur1->val += cur2->val;

            // 先判断左右孩子都非空的情况,即判断是否为重叠的结点,如果是,则入队
            if (cur1->left && cur2->left){
                // root1和 root2的左孩子都非空,要进行比较,加入队列
                layer.push(cur1->left);
                layer.push(cur2->left);
            }
            if (cur1->right && cur2->right){
                // root1和 root2的右孩子都非空,要进行比较,加入队列
                layer.push(cur1->right);
                layer.push(cur2->right);
            }

            // 顺序不可颠倒!!!

            // 再判断如果 root1 的左右孩子空,而 root2 的左右孩子非空的情况
            if (!cur1->left && cur2->left){
                // root1左孩子空,root2左孩子非空
                cur1->left = cur2->left;
            }
            if (!cur1->right && cur2->right){
                // root1右孩子空,root2右孩子非空
                cur1->right = cur2->right;
            }
        }
        return root1;
    }
};

为什么顺序不可以颠倒?让我们来假设顺序颠倒的情况(注意,以下代码是错误示范!):

// 如果先判断 root1 的左孩子空,而 root2 的左孩子非空的情况
if (!cur1->left && cur2->left){
    cur1->left = cur2->left;
    // 此时本来 cur1 的左孩子为空,在这里补齐了
    // 则cur1的左孩子变为非空了
}

// 再判断左孩子都非空的情况
// 由于前面先补齐了cur1的左孩子,这里就满足了cur1的左孩子和cur2的左孩子都非空的情况
// 这时候两个左孩子都入队了,在后续的层序遍历中,程序会认为入队的两个结点就是重叠的结点,将两个节点值相加
// 但这两个结点并不属于重叠的结点!不需要相加!
if (cur1->left && cur2->left){
    layer.push(cur1->left);
    layer.push(cur2->left);
}

二叉搜索树中的搜索

题干

题目:给定二叉搜索树(BST)的根节点 root 和一个整数值 val。你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

链接:. - 力扣(LeetCode)

思路和代码

根据二叉搜索树的性质,将要查询的值 val 和根节点比较,比根节点小的查询左子树,比根节点的查询右子树。

递归法
  • 递归参数:参数是传入的要查询的树的根节点,要查询的值;返回值是查询到的结点。

  • 递归结束的条件:当传入的结点为空,说明已经没找到,直接返回空指针;如果找到了,直接返回当前结点。

  • 递归顺序:前序遍历,根据 “中左右” 的顺序,先查询中间结点,再递归查询左子树、右子树。

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if (root == nullptr) return nullptr;
        if (val == root->val) return root;
        else if (val < root->val) return searchBST(root->left,val); // 递归查询左子树
        else return searchBST(root->right,val); // 递归查询右子树
    }
};
迭代法

使用一个指针 cur 遍历二叉树,将要查询的值 val 和当前结点 cur 比较,比根节点小的查询左子树,比根节点的查询右子树。

二叉搜索树在遍历查询时不需要回溯!这和在一般二叉树中查询结点不同,因为二叉搜索树的结点是有序的,直接就能确定搜索的方向。

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        TreeNode* cur = root;
        while (cur){
            if (val == cur->val){
                break;
            } else if (val < cur->val){
                cur = cur->left; // 遍历左子树查询
            } else{
                cur = cur->right; // 遍历右子树查询
            }
        }
        return cur;
    }
};

验证二叉搜索树

题干

题目:给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。

  • 节点的右子树只包含 大于 当前节点的数。

  • 所有左子树和右子树自身必须也是二叉搜索树。

链接:. - 力扣(LeetCode)

思路和代码

方法一:递归法,中序遍历

这个问题没有那么简单,单纯将问题分解为每个结点的左右子树是否都是二叉搜索树是行不通的。

陷阱一:不能单纯的比较左节点小于中间节点,右节点大于中间节点。

正确思路:要比较 左子树所有节点小于中间节点,右子树所有节点大于中间节点

解决方法:需要定义全局变量 maxValue 来记录遍历过的最大值,按照 “左中右” 的顺序,后来遍历的元素都要比这个最大值要大。

陷阱二:一般我们都将 maxValue 初始化为 int 的最小值,再判断后来的结点是否都比 maxValue 要大。但样例中整棵树的最小节点也有可能是 int 的最小值,此时无法比较大小。

解决方法:将 maxValue 初始化为 longlong 的最小值,这样就比 int 的最小值还要小。

  • 递归参数和返回值:参数是传入的树的根结点,返回值是判断是否为二叉搜索树的 bool 型值。

  • 递归结束条件:当传入结点为空时,返回真;

  • 递归顺序:采用中序遍历,先判断左子树是否为二叉搜索树,在递归左子树的过程中不断更新左子树的最大值 maxValue,左子树遍历完回到中间结点时,要保证中间结点比左子树的最大值还要大,并更新最大值 maxValue 为中间节点值;若中间节点没有比 maxValue 大,说明不是二叉搜索树,直接返回false。中间结点处理完要继续遍历右子树,判断右子树是否为二叉搜索树。如果左右子树都为二叉搜索树,则返回 true。

class Solution {
public:
    long long maxValue = LONG_MIN; // 记录遍历过程中的最大值
    bool isValidBST(TreeNode* root) {
        if (root == nullptr) return true;
        bool isLeftBST = isValidBST(root->left);
        // 由于先遍历了左子树,我们需要不断更新左子树的最大值 maxValue
        // 二叉搜索树要保证后来的结点都比整个左子树都大
        // 只需要保证当前结点比左子树的最大值 maxValue 大
        if (root->val > maxValue){
            maxValue = root->val;
            // 更新最大值为中间结点
        } else {
            return false;
        }
        // 在遍历右子树之前,我们已经用 maxValue记录了左子树和中间节点的最大值
        // 要保证所有右子树的结点都要比 maxValue 大才满足二叉搜索树
        bool isRightBST = isValidBST(root->right);
        return isLeftBST&&isRightBST;
    }
};
方法二:求中序序列

如果当前二叉树是有效的二叉搜索树,则它的中序遍历序列一定是升序排列,且没有重复的元素。只需要求出中序序列即可。

class Solution {
public:
    void inorder(TreeNode* root, vector<int> &nums){
        if (root == nullptr) return;
        inorder(root->left,nums);
        nums.push_back(root->val);
        inorder(root->right,nums);
    }
    bool isValidBST(TreeNode* root) {
        vector<int> nums; // 存储中序序列
        inorder(root,nums);
        // 判断中序序列是否升序
        for (int i = 0; i < nums.size()-1; ++i) {
            if (nums[i] >= nums[i+1])
                return false; // 只要不是升序,直接返回 false
        }
        return true;
    }
};

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

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

相关文章

Linux下载卸载MySql

一. 安装Mysql 1.下载mysql --- 密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 rpm -ivh http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm yum -y --enablerepomysql57-community install mysql-community-server 2.启动mysql sy…

如何选择最佳智能排班系统?9款工具全解析

本文介绍的自助排班管理工具有Moka、红圈排班、工作易排班、泛微e-office、Udesk、畅捷通T、Zenefits、Homebase、Deputy。 在管理团队时&#xff0c;手动排班不仅耗时&#xff0c;还容易出错&#xff0c;影响工作效率&#xff0c;相信很多管理者也有同样的困扰。为了解决这个痛…

【Spring Boot】定时任务

目录 前言 定时任务注解Scheduled 设计一个定时任务 1.启用定时任务 2.创建定时任务 Cron 表达式详解 多线程定时任务 总结 定时任务框架xxl-job SpringBoot继承定时任务框架 1.搭建调度中心xxl-job-admin 1.1下载项目 1.2修改配置文件端口和数据库代码 1.3连接到…

Linux进程间通信学习记录(无名管道)

0.Linux进程间通信的方式 &#xff08;1&#xff09;.从UNIX继承过来的通信方式 无名管道&#xff08;pipe&#xff09; 有名管道&#xff08;fifo&#xff09; 信号&#xff08;signal&#xff09; &#xff08;2&#xff09;.System V IPC 共享内存 消息队列 信号灯集 &am…

Java方法03:方法的重载

本节内容视频链接&#xff1a;https://www.bilibili.com/video/BV12J41137hu?p47&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5https://www.bilibili.com/video/BV12J41137hu?p47&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 在Java中&#xff0c;‌方法的重载&#x…

AI编程系列一1小时完成链家房价爬虫程序

背景 AI编程实在太火&#xff0c;写了很多年的Java&#xff0c;现在Python 和Go 简单好用&#xff0c;今天结合智谱清言快速完成一个程序爬虫程序&#xff0c;没有任何Python 编程经验&#xff0c;只需要会提问&#xff0c;熟悉简单HTML结构即可。未来一定是有业务能力者的福…

Anylogic设置颜色

三维对象的颜色修改 以detector智能体为例&#xff0c;颜色修改代码为&#xff1a; detector.setColor(“Material_(4)_Surf”,blue); 二维对象的颜色修改 house智能体为例&#xff0c;对组件内的_ps282填充的颜色进行修改&#xff0c;level_是该智能体已有的参数或者称之为变…

CLRerNet推理详解及部署实现(下)

目录 前言一、CLRerNet推理(Python)1. CLRerNet预测2. CLRerNet预处理3. CLRerNet后处理4. CLRerNet推理 二、CLRerNet推理(C)1. ONNX 导出2. CLRerNet预处理3. CLRerNet后处理4. CLRerNet推理 三、CLRerNet部署1. 源码下载2. 环境配置2.1 配置CMakeLists.txt2.2 配置Makefile …

python:画由抛物线: y^2=2x 与直线 y=x-4 所围成的图形

《高等数学》同济大学版 P339 编写 test_diff_3_area.py 如下 # -*- coding: utf-8 -*- """ 画由抛物线: y^22x 与直线 yx-4 所围成的图形 """ import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Polygon…

11结构型设计模式——外观模式

一、外观模式简介 外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它提供了一个统一的接口来访问子系统中的一组接口&#xff0c;使得子系统的使用更加简单和方便。通过外观模式&#xff0c;可以将复杂的子系统封装在一个外观类&#xff08;…

​​数据结构-树

n = 度数之和 + 1 n + x + y + z = x +2y +3z + 1

增加练习(修改获取练习的基本信息接口)

文章目录 1.sun-club-practice-api1.enums1.CompleteStatusEnum.java 2.req1.GetPracticeSubjectsReq.java 3.vo1.PracticeSubjectListVO.java 2.sun-club-practice-server1.PracticeSetController.java2.service1.PracticeSetServiceImpl.java 3.dao1.PracticeDao.java2.Pract…

HAL STM32 SG90舵机驱动控制

HAL STM32 SG90舵机驱动控制 &#x1f516;测试对象&#xff1a;STM32F103SG90舵机 &#x1f33c;功能实现&#xff1a;通过串口指令&#xff0c;控制SG90舵机转动到指定角度。 ✨在实际硬件舵机驱动过程中&#xff0c;使用SG90普通舵机空载运转情况下&#xff0c;电流在180mA…

验证集的loss比训练集大得多Val Loss is too large

这个跟数据集有关&#xff0c;不过可已通过clip减缓。 解决方法 nn.utils.clip_grad_norm_(self.Model.parameters(), max_norm5)

AtCoder Regular Contest 182 A~D

A.Chmax Rush!&#xff08;枚举&#xff09; 题意&#xff1a; 有一个长度为 N N N的整数序列 S S S。最初&#xff0c; S S S的所有元素都是 0 0 0。 同时给你两个长度为 Q Q Q的整数序列&#xff1a; P ( P 1 , P 2 , … , P Q ) P(P_1,P_2,\dots,P_Q) P(P1​,P2​,…,PQ…

AI产品经理修炼指南:从青铜到王者的逆袭之路

一、AI通识 1.1 AI产业结构 AI发展至今大致按照在产业结构上的分工不同产生了三种类型的公司&#xff0c;我们在转型时最好要先明确自己的优势及兴趣&#xff0c;来判断自己适合着眼于哪个层面的工作&#xff0c;从而进行针对性的学习和提升。 &#xff08;1&#xff09;行业…

Apache Flink细粒度资源管理原理

粗粒度资源管理 Apache Flink 1.1.4版本之前使用的是粗粒度的资源管理&#xff0c;即每个算子Slot Request所需要的资源都是未知的&#xff0c;Flink内部用UNKNOWN的特殊值来表示&#xff0c;这个值可以和任意资源规则的物理Slot匹配&#xff0c;站在Taskmanager的角度&#x…

打卡学习Python爬虫第二天|Web请求过程刨析

一、服务器渲染 服务器端渲染&#xff08;Server-Side Rendering&#xff0c;简称SSR&#xff09;是一种网页渲染技术。在这种技术中&#xff0c;服务器在接收到客户端的请求后&#xff0c;会生成页面的初始HTML内容&#xff0c;并将其发送给客户端。客户端浏览器接收到这些HT…

什么是BKP(备份寄存器)

一&#xff0c;什么是BKP 备份寄存器是42个16位的寄存器&#xff0c;可用来存储84个字节的用户应用程序数据。他们处在备份域里&#xff0c;当VDD电源被切断&#xff0c;他们仍然由VBAT维持供电。当系统在待机模式下被唤醒&#xff0c;或系统复位或电源复位时&#xff0c;他们也…

数据结构(6.2_2)——领接表法

领接表法&#xff08;顺序存储链式存储&#xff09; 代码&#xff1a; #define MaxVertextNum 10 //边(弧) typedef struct ArcNode {int adjvex;//边/弧指向哪个结点struct ArcNode* next;//指向下一条弧的指针//InfoType info;//边权值 }ArcNode; //顶点 typedef struct VNo…