首页 > 编程语言 >24暑假算法刷题 | Day21 | LeetCode 669. 修剪二叉搜索树,108. 将有序数组转换为二叉搜索树,538. 把二叉搜索树转换为累加树

24暑假算法刷题 | Day21 | LeetCode 669. 修剪二叉搜索树,108. 将有序数组转换为二叉搜索树,538. 把二叉搜索树转换为累加树

时间:2024-07-24 15:26:47浏览次数:15  
标签:right 转换 cur 二叉 搜索 TreeNode root 节点 left

目录


669. 修剪二叉搜索树

点此跳转题目链接

题目描述

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

示例 1:

在这里插入图片描述

输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]

示例 2:

在这里插入图片描述

输入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3
输出:[3,2,null,1]

提示:

  • 树中节点数在范围 [1, 104]
  • 0 <= Node.val <= 104
  • 树中每个节点的值都是 唯一
  • 题目数据保证输入是一棵有效的二叉搜索树
  • 0 <= low <= high <= 104

题解

首先试试递归。第一步考虑递归出口,对于当前正在处理的节点:

  • 若为空节点,直接返回

  • 若节点值小于 low ,返回其右节点

    此时,当前节点的左子树中的值,全都小于当前节点值,自然也全都小于 low ,故可以直接修剪掉左子树,用右子树取代当前(根)节点。下面情况同理。

  • 若节点值大于 high ,返回其左节点

否则,当前节点处于目标区间内,递归处理其左右子树即可。整体代码如下:

TreeNode *trimBST(TreeNode *root, int low, int high)
{
    // 空节点直接返回
    if (!root)
        return nullptr;
    // 节点值不在目标区间
    if (root->val < low) {
        TreeNode *right = trimBST(root->right, low, high);
        return right;
    }
    if (root->val > high) {
        TreeNode *left = trimBST(root->left, low, high);
        return left;
    }
    // 节点值在目标区间
    root->left = trimBST(root->left, low, high);
    root->right = trimBST(root->right, low, high);
    return root;
}

接下来再试试迭代法,需要注意的是 ⚠️

  • 一开始就要让 root 指针先走到目标区间内
  • 迭代时注意,修剪掉某棵子树后,新接过来的子树可能还是不满足条件,故此处的逻辑判断不能只用一个 if ,而要用 while

整体代码如下:

TreeNode *trimBST(TreeNode *root, int low, int high)
{
    // 先让root指针走到目标区间内的值
    while (root && (root->val < low || root->val > high)) {
        if (root->val < low)
            root = root->right;
        if (root->val > high)
            root = root->left;
    }
    // 此时的root已经处于目标区间内了,处理其左子树
    TreeNode *cur = root;
    while (cur) {
        while (cur->left && cur->left->val < low) // 注意此处要用while判断
            cur->left = cur->left->right;
        cur = cur->left;
    }
    // 此时的root已经处于目标区间内了,处理其右子树
    cur = root;
    while (cur) {
        while (cur->right && cur->right->val > high) // 注意此处要用while判断
            cur->right = cur->right->left;
        cur = cur->right;
    }
    return root;
}

108. 将有序数组转换为二叉搜索树

点此跳转题目链接

题目描述

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵平衡二叉搜索树。

平衡二叉树 是指该树所有节点的左右子树的深度相差不超过 1。

示例 1:

在这里插入图片描述

在这里插入图片描述

输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案

示例 2:

在这里插入图片描述

输入:nums = [1,3]
输出:[3,1]
解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums严格递增 顺序排列

题解

题目要求生成的BST是 平衡 的,即要尽量让各节点的左右子树高度相同,故联想到 二分 的思想:

  • 每次取当前数组的中位数作为根节点
  • 其左边部分的子数组递归生成其左子树
  • 其右边部分的子数组递归生成其右子树

可以任意写一个简单的递增序列,模拟一下,就理解了。

代码(C++)

TreeNode *getRoot(const auto &nums, int left, int right) {
    // 递归出口:左右指针错开
    if (left > right)
        return nullptr;
    // 当前数组切片的中位数作为根节点
    int mid = left + (right - left) / 2;
    TreeNode *root = new TreeNode(nums[mid]);
    root->left = getRoot(nums, left, mid - 1);
    root->right = getRoot(nums, mid + 1, right);
    return root;
}

TreeNode *sortedArrayToBST(vector<int> &nums)
{
    return getRoot(nums, 0, nums.size() - 1);
}

也可以考虑迭代法,但是需要用多个数据结构存储子数组的左右指针和树节点等,代码复杂不少,这里参考 代码随想录 上Carl的解法:

TreeNode* sortedArrayToBST(vector<int>& nums) {
    if (nums.size() == 0) return nullptr;

    TreeNode* root = new TreeNode(0);   // 初始根节点
    queue<TreeNode*> nodeQue;           // 放遍历的节点
    queue<int> leftQue;                 // 保存左区间下标
    queue<int> rightQue;                // 保存右区间下标
    nodeQue.push(root);                 // 根节点入队列
    leftQue.push(0);                    // 0为左区间下标初始位置
    rightQue.push(nums.size() - 1);     // nums.size() - 1为右区间下标初始位置

    while (!nodeQue.empty()) {
        TreeNode* curNode = nodeQue.front();
        nodeQue.pop();
        int left = leftQue.front(); leftQue.pop();
        int right = rightQue.front(); rightQue.pop();
        int mid = left + ((right - left) / 2);

        curNode->val = nums[mid];       // 将mid对应的元素给中间节点

        if (left <= mid - 1) {          // 处理左区间
            curNode->left = new TreeNode(0);
            nodeQue.push(curNode->left);
            leftQue.push(left);
            rightQue.push(mid - 1);
        }

        if (right >= mid + 1) {         // 处理右区间
            curNode->right = new TreeNode(0);
            nodeQue.push(curNode->right);
            leftQue.push(mid + 1);
            rightQue.push(right);
        }
    }
    return root;
}

538. 把二叉搜索树转换为累加树

点此跳转题目链接

题目描述

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

  • 节点的左子树仅包含键 小于 节点键的节点。
  • 节点的右子树仅包含键 大于 节点键的节点。
  • 左右子树也必须是二叉搜索树。

注意: 本题和 1038: https://leetcode-cn.com/problems/binary-search-tree-to-greater-sum-tree/ 相同

示例 1:

img
输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

示例 2:

输入:root = [0,null,1]
输出:[1,null,1]

示例 3:

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

示例 4:

输入:root = [3,2,4,1]
输出:[7,9,4,10]

提示:

  • 树中的节点数介于 0104 之间。
  • 每个节点的值介于 -104104 之间。
  • 树中的所有值 互不相同
  • 给定的树为二叉搜索树。

题解

首先明确一下累加思路:

由于大于等于最大节点的累计值,就是该节点的值本身,所以应该从最大的数开始累加,同时维护一个累计值——每次将累计值加上当前节点值。

比如之后,大于等于第二大节点值的累计值,就是第一次的累计值,加上第二大节点的值。以此类推,就能得到所有节点的新值。

可以结合题目描述中示例1的图进行模拟,便于理解

那么我们自然需要按照节点值的大小,从大到小处理。可以考虑用一个优先队列,按照节点值从大到小存储各节点,然后依次取出队头元素处理:

TreeNode *convertBST(TreeNode *root)
{
    if (!root)
        return nullptr;
    // 用一个优先队列,按原节点值从大到小存储所有节点
    auto cmp = [](TreeNode *a, TreeNode *b) {
        return a->val < b->val;
    };
    priority_queue<TreeNode *, vector<TreeNode *>, decltype(cmp)> pq(cmp);
    // 层序遍历
    queue<TreeNode *> q;
    q.push(root);
    while (!q.empty())
    {
        int size = q.size();
        for (int i = 0; i < size; i++)
        {
            TreeNode *cur = q.front();
            q.pop();
            pq.push(cur); // 将节点加入优先队列
            if (cur->left)
                q.push(cur->left);
            if (cur->right)
                q.push(cur->right);
        }
    }
    // 按原节点值从大到小更新节点值
    int biggerSum = 0; // 大于等于当前节点值的所有节点值之和
    while (!pq.empty())
    {
        biggerSum += pq.top()->val;
        pq.top()->val = biggerSum;
        pq.pop();
    }
    return root;
}

不过上述算法实际上可以应用于任意二叉树,也就是说它并没有利用二叉搜索树的性质特点。我们知道,二叉搜索树的中序遍历结果是一个递增序列,那么将它反过来,不就是我们需要的节点值递减序列了吗?

所以,我们可以采取逆中序遍历,在遍历的过程中完成累计和更新操作。代码和中序遍历差不多,调整一下顺序即可。若采用递归:

int biggerSum = 0;
TreeNode *convertBST(TreeNode *root)
{
    // 逆中序遍历——右中左
    if (!root)
        return nullptr;

    root->right = convertBST(root->right); // 右
    biggerSum += root->val;                // 更新节点值之和
    root->val = biggerSum;                 // 中
    root->left = convertBST(root->left);   // 左

    return root;
}

若采用迭代:

TreeNode *convertBST(TreeNode *root)
{
    // 基于统一迭代法
    if (!root)
        return nullptr;
    int biggerSum = 0;
    stack<TreeNode *> st;
    st.push(root);
    while (!st.empty())
    {
        TreeNode *cur = st.top();
        st.pop();
        if (cur)
        {
            // 要得到“右中左”的逆中序遍历结果,则应按照“左中右”入栈
            if (cur->left)
                st.push(cur->left); // 左
            st.push(cur);           // 中
            st.push(nullptr);       // 空节点标记
            if (cur->right)
                st.push(cur->right); // 右
        }
        else
        {
            biggerSum += st.top()->val;
            st.top()->val = biggerSum; // 更新节点值
            st.pop();
        }
    }
    return root;
}

标签:right,转换,cur,二叉,搜索,TreeNode,root,节点,left
From: https://blog.csdn.net/weixin_54468359/article/details/140659507

相关文章

  • 温度转换问题
    代码:关键算法完成摄氏度和华氏度的转换TempSTr=input("请输入带有符号的温度值:")ifTempSTr[-1]in['F','f']:C=(eval(TempSTr[0:-1])-32)/1.8print("转换后的温度是{:.2f}C".format(C))elifTempSTr[-1]in['C','c']:......
  • Qt实现图片拖拽上传过滤文件夹内图片自动搜索列表展示
     1.功能实现支持图片、或者文件夹拖拽上传,会自动获取文件夹中的图片。对拖入的文件做格式判断,不符合格式要求的会不支持拖入,拖入后展示在list列表中,可以进行删除,和上下滚动查看;#ifndefDRAGDROPPIC_H#defineDRAGDROPPIC_H#include<QWidget>#include"ui_DragDropPic.h......
  • 微信小程序 - 最新详细实现集成腾讯地图配置流程及使用教程,基于腾讯位置服务做地图标
    前言网上的教程代码太乱了,并且很少有真实请求的示例,本文提供优质配置教程及示例源码。在微信小程序开发中,详解实现接入腾讯地图教程,后台配置完整流程及使用教程,附带腾讯地图显示渲染和地图标记点,获取本机当前定位省市区或精确的经纬度,IP属地定位获取城市名称/市区名,将经......
  • 尝试使用 pyinstaller 将 python 文件转换为可执行文件时出现 TypeError
    稍后的目的是通过命令行向GPT4all发送问题并将答案存储在文本文档中。我想将阻止代码转换为exe,但它产生了TypeError。这是到目前为止的代码:fromgpt4allimportGPT4Allmodel=GPT4All("Meta-Llama-3-8B-Instruct.Q4_0.gguf",device='cpu')#downloads/loads......
  • 如何将字符串转换为十进制数字?
    我有一个包含以下格式字符串的python列表:A1=['"29.0"','"65.2"','"75.2"']如何将这些字符串转换为十进制数字以对列表元素执行算术运算?可以使用float()函数将字符串转换为十进制数。以下是如何执行此操作的示例:A1=['"29.0"',�......
  • 掌握时间与空间:深入探讨Golang中的时间戳与时区转换
    时间是我们生活的基石,而在计算机科学中,时间处理显得尤为重要。尤其是当你在处理分布式系统、跨时区应用和全球服务时,时间和时区的管理变得不可或缺。在这篇文章中,我们将以幽默和深入的方式探讨Golang中的时间戳与时区转换。时间的基本概念时间戳时间戳(Timestamp)是指从1970年1月......
  • 程序员福音-英文大小写转换,驼峰下划线空格小数点互转
    在日常的开发工作中,我们常常需要将文本转换为不同的格式,包括大小写转换、驼峰式和下划线格式之间的转换、空格和小数点之间的转换等。为了提高工作效率,我们可以使用一些工具来实现这些操作。在线英文大小写,驼峰转下划线,空格下划线转换-无双工具这个工具是一个免费的在线工......
  • 41-50题矩阵和字符串 在Java中,将大写字符转换为小写字符的方法主要有以下几种:
    20240723一、数组最后几个和字符串的两个448.找到所有数组中消失的数字(和645.错误的集合差不多)283.移动零118.杨辉三角119.杨辉三角II661.图片平滑器(没看懂)598.区间加法II566.重塑矩阵303.区域和检索-数组不可变520.检测大写字母125.验证回文串二、在Jav......
  • AD模数转换(ADC)在音频处理中的详细深度讲解
    AD模数转换(Analog-to-DigitalConversion,简称ADC)是将模拟信号转换为数字信号的过程。对于音频处理来说,ADC是音频录制、数字音频处理和传输的关键步骤。以下是对AD模数转换在音频方面的详细讲解:1.ADC的基本原理ADC的过程包括以下几个步骤:采样(Sampling):将连续变化的模拟信号在时......
  • Texstudio正反向搜索-配合sumatraPDF
    选项->设置->命令,然后找到外部pdf查看器,输入代码:"C:\Users\Kevin\AppData\Local\SumatraPDF\SumatraPDF.exe"-forward-search"?c:am.tex"@-inverse-search"C:\ProgramFiles\texstudio\texstudio.exe%%f-line%%l""?am.pdf"......