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

news2024/9/20 10:44:00

目录

  • 669. 修剪二叉搜索树
    • 题目描述
    • 题解
  • 108. 将有序数组转换为二叉搜索树
    • 题目描述
    • 题解
  • 538. 把二叉搜索树转换为累加树
    • 题目描述
    • 题解


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

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

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

相关文章

Mac应用快速启动器:Alfred 5 for Mac 激活版

Alfred 5 是一款专为 macOS 系统设计的效率提升工具。这款软件以其快速启动和高效操作功能著称&#xff0c;通过使用快捷键来呼出输入界面&#xff0c;用户可以快速完成各种任务。 最新版本 Alfred 5.5 引入了一些新功能。其中包括整合了 ChatGPT 和 DALL-E&#xff0c;这意味…

Map的常见API

Map的常见API Map是双列集合的顶层接口&#xff0c;它的功能是全部双列集合都可以继承使用的 Map的遍历方式 map的遍历方式(键找值) package demo3;import java.util.HashMap; import java.util.Map; import java.util.Set;public class a1 {public static void main(String…

如何优化网站以提升UX设计质量

什么叫 UX 设计&#xff1f;UX 设计&#xff0c;即用户体验设计&#xff0c;是指为提升用户体验而进行的产品设计。 UX 在设计中&#xff0c;设计师通过调查和研究用户来掌握用户的需求和喜好&#xff0c;并利用这些信息来设计产品。设计师还会测试产品&#xff0c;以确保它们能…

xftp 如何链接 ubutn

一 如果你的ubutun 没有安装 openssh-server&#xff0c;那么是链接不上的&#xff0c;因此要先在ubutun 上一定要先安装openssh-server sudo apt install openssh-server 二 查看 ubutun 的ip 三 配置 xftp

导入vue3-引入富文本框编辑器-@wangeditor/editor库

效果图 下载依赖包 pureadmin/utils": "2.4.7 wangeditor/editor": "^5.1.23 wangeditor/editor-for-vue": "5.1.12定义公共组件 在src目录下定义 components/ReEditor/index.vue index.ts import editor from "./src/Editor.vue"…

BFS实现迷宫最短路径

结合队列的知识利用 广度优先遍历&#xff0c;通过对能走的路径的记录以及对走过路径的标记&#xff0c;进行多条路搜查 一、理论基础 如下图的迷宫&#xff1a; 选取所走方向&#xff08;针对某一个位置&#xff09;下&#xff0c;右&#xff0c;上&#xff0c;左&#xff0…

客户在哪儿AI——做真正管用的大客户获客方案

我们的目标是要打造一个真正“管用”的ToB大客户获客方案。以下是两个100%真实的案例&#xff0c;所有数据均为真实经营数据。一个是证明客户在哪儿AI对市场工作的颠覆性提升&#xff0c;另一个是证明客户在哪儿AI对决策层和销售工作的颠覆性提升。 客户在哪儿AI生产的是企业全…

C++模板——泛型编程

目录 1. 什么是泛型编程 2. 函数模板 2.1 定义格式 2.2 实例化及原理 2.3 参数匹配原则 3. 类模板 3.1 定义格式 3.2 实例化 4. 非类型模板参数 5. 模板的特化 5.1 概念 5.2 函数模板和类模板特化 6. 模板的分离编译 1. 什么是泛型编程 如何实现一个通用的加…

Windows下Pytorch入门深度学习环境安装与配置(CPU版本)

Windows下Pytorch入门深度学习环境安装与配置&#xff08;CPU版本&#xff09; 一、安装过程中各个软件的作用&#xff08;一&#xff09;Python&#xff08;二&#xff09;库 / 包 / package / library&#xff08;三&#xff09;PyTorch / Tensorflow&#xff08;四&#xff…

详解yolov5和yolov8以及目标检测相关面试

一、与yoloV4相比&#xff0c;yoloV5的改进 输入端&#xff1a;在模型训练阶段&#xff0c;使用了Mosaic数据增强、自适应锚框计算、自适应图片缩放基准网络&#xff1a;使用了FOCUS结构和CSP结构Neck网络&#xff1a;在Backbone和最后的Head输出层之间插入FPN_PAN结构Head输出…

计算机二级题--文件 章节

之前写的有文件全部的知识点&#xff0c;这一篇主要针对计算机二级真题的整理。 需要备考计算机二级的小伙伴们先收藏起来吧。整理不易&#xff0c;不过有帮助记得点赞哦 1.相关概念考点&#xff1a; 1.文件指针指向的是文件缓冲区的位置&#xff0c;不是文件在读位置。 2.…

儿童洗衣机哪个牌子好?五大爆款机型倾情分享

在当今繁忙的生活中&#xff0c;儿童洗衣机已成为我们日常生活中不可或缺的家电。但是&#xff0c;面对市场上众多品牌的儿童洗衣机&#xff0c;那么&#xff0c;到底儿童洗衣机哪个牌子好&#xff1f;本次我将在这篇文章中探讨儿童洗衣机的选购策略&#xff0c;以帮助大家找到…

记录unraid docker更新的域名

背景&#xff1a;级联 一、安装内容 unraid更新docker&#xff0c;之前一直失败&#xff0c;修改网络后可以进行安装。 二、查看域名 查看域名&#xff0c;发现是走github的&#xff0c;怪不得有一些docker无法正常更新 三、解决方法 更改代理&#xff0c;这里为unraid的…

智慧教育解决方案

1. 智慧教育解决方案概述 智慧教育解决方案旨在通过教育改革实现四个转变&#xff1a;从以教为主到以学为主&#xff0c;从专业教育到通识与专业教育结合&#xff0c;从课堂教育到课内外结合&#xff0c;以及从结果评价到过程与结果评价结合。 2. 教育三维目标 教育三维目标…

避免工件精度受损:精准调整滚珠丝杆状态!

滚珠丝杆作为一种常用的传动装置&#xff0c;经过长期使用或者维护不当的情况下有时会发生弯曲或变形。弯曲不仅会对滚珠丝杆的精度和寿命造成影响&#xff0c;而且还会对整个机器系统的工作效率和安全性产生影响。那么&#xff0c;应该如何要避免滚珠丝杆弯曲对工件的影响呢&a…

Java数据结构与算法--链表(Linked List)

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;Java SE关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 深入了解链表&#xff1a; 链表是一种常见的数据结构&#xff0c;它由一系列节点…

【SOC 芯片设计 DFT 学习专栏 -- DFT DRC规则检查】

请阅读【嵌入式及芯片开发学必备专栏】 请阅读【芯片设计 DFT 学习系列 】 如有侵权&#xff0c;请联系删除 转自&#xff1a; 芯爵ChipLord 2024年07月10日 12:00 浙江 文章目录 概述DRC的概念Tessent DRC检查的概述时钟相关检查扫描相关检查BIST规则检查预DFT时钟规则检查 …

zh echarts样式

记录一下&#xff1a; 一个图的配置 在echarts官网demo界面 option {title: {text: },legend: {data: [xxx前, xxx后]},radar: {// shape: circle,name: {// 雷达图各类别名称文本颜色textStyle: {color: #000,fontSize: 16}},indicator: [{ name: 完整性, max: 1 },{ name:…

ByteBuffer调试工具类

一个可以形象展示ByteBuffer内容的方法&#xff0c;便于调试 package com.example.netty;import java.nio.ByteBuffer;public class ByteBufferUtil {/*** 打印ByteBuffer的内容&#xff0c;以十六进制和ASCII字符的形式展示。** param buffer 要展示的ByteBuffer*/public sta…

Java全栈课程之Linux——用户组管理

每个用户都有一个用户组&#xff0c;系统可以对一个用户组中的所有用户进行集中管理。不同Linux 系统对用户组的规定有所不同&#xff0c;如Linux下的用户属于与它同名的用户组&#xff0c;这个用户组在创建用户时同时创建。 用户组的管理涉及用户组的添加、删除和修改。组的增…