Leecode 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]

递归法解题思路与代码

本题要求修剪二叉搜索树中大于和小于目标区间的节点,最基本的是需要知道二叉搜索树的性质,其向下投影(中序遍历)即为有序序列。其次的关键在于要对每个节点的可能情形都进行分类讨论,此时需要注意不能漏掉某一情形,同时要清晰对于每种情况都要如何操作。首先我们先讨论如何来进行分情况讨论。

每个节点的值可能取到的大小0 <= low <= high <= 10^4,同时我们需要保留其中取值在[low, high]闭区间内的值,那么就相当于把数轴划分为三段,分别为:

  • [0,low),在代码中可以具体表示为Node->val < low
  • [low, high],即Node->val >= low && Node->val <= high
  • (high, 10^4],即Node->val > high

根据上面的分类情况,就能考虑到每一个节点的所有情况,接下来我们再说明对于其中每一种情况需要如何处理:

  • Node->val < low时,当前节点及其左子树都不满足条件,但是右子树是否满足还未知。因此可以考虑使用右子节点来替代当前节点,继续递归处理当前节点
  • Node->val >= low && Node->val <= high时,当前节点满足条件,此时递归处理左右子树
  • Node->val > high时,类似第一种情况,右子树和当前节点都不满足,用左子节点来替代当前节点。并再将当前节点传入递归

根据上面算法思想,即可实现如下代码:

class Solution {
public:
void cutHelper(TreeNode*& curNode, int low, int high){
if(!curNode) return; // 如果当前节点已经为空,则直接返回
if(curNode->val < low) { // 如果当前节点已经小于下界,则用其右子节点来替代当前节点
if(curNode->val < low)curNode = curNode->right; // 用右子节点来替代当前节点
if(curNode)cutHelper(curNode, low, high); // 继续将当前节点传入递归修剪(因当前节点已经被替换,所以继续传当前节点)
}
else if(curNode->val >= low && curNode->val <= high){ // 如果当前节点满足条件,则需要分别对左右子树进行修剪
cutHelper(curNode->left, low, high); // 递归修剪左子树
cutHelper(curNode->right, low, high); // 递归修剪右子树
}
else if(curNode->val > high){ // 如果当前节点的值大于上界,则将
if(curNode->val > high) curNode = curNode->left;
if(curNode)cutHelper(curNode, low, high);
}
} TreeNode* trimBST(TreeNode* root, int low, int high) {
cutHelper(root, low, high);
return root;
}
};

上面代码的最坏时间复杂度为\(O(n)\),即每个节点都需要进行一次判断,判断其是否落在区间内。但实际上每次判断后根据二叉搜索树的性质,可以知道当前节点左右子树是否有可能满足,从而省略一部分判断用时从而减少时间复杂度。

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

题目描述

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

  • 示例 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,从而确保了平衡性。

class Solution {
public:
void buildHelper(TreeNode*& curRoot, vector<int> curVec){
if(curVec.empty()) return; // 如果当前向量已经为空,则直接返回
int cutPoint = curVec.size()/2 ; // 使用有序数组的中点作为分割点
curRoot = new TreeNode(curVec[cutPoint]); // 使用中点值来建立当前节点 vector<int> leftVec(curVec.begin(), curVec.begin()+cutPoint); // 建立左子数组
buildHelper(curRoot->left, leftVec); // 递归建立左子树 vector<int> rightVec(curVec.begin()+cutPoint+1,curVec.end()); // 建立右子数组
buildHelper(curRoot->right, rightVec); // 递归建立右子树 return;
} TreeNode* sortedArrayToBST(vector<int>& nums) {
TreeNode* root = nullptr; // 讨论空节点的情况
buildHelper(root, nums); // 调用递归函数进行建树
return root;
}
};

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

题目描述

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

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

节点的左子树仅包含键 小于 节点键的节点。

节点的右子树仅包含键 大于 节点键的节点。

左右子树也必须是二叉搜索树。

  • 示例 1:



输入:[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]

递归法 解题思路与代码

本题使用递归来进行深度优先算法。因为要将每个节点转换为本身与右边所有节点的和,所以可以知道一开始必须走到最左边的那个节点并记录求和值。只要能够想通深度优先就是中序遍历,那么本题就没有什么难度了。直接在中序遍历递归的基础上,将其更改为记录求和并更新当前节点的操作即可。故有下面代码:

class Solution {
public:
void convertHelper(TreeNode* curNode, int& sum){ // 使用中序遍历来进行递归
if(!curNode) return; // 如果当前节点为空则返回
convertHelper(curNode->right, sum); // 向右递归 sum += curNode->val; // 将当前节点值加到sum上
curNode->val = sum; // 在令当前节点值等于sum convertHelper(curNode->left, sum); // 向左递归
} TreeNode* convertBST(TreeNode* root) {
int sum = 0;
convertHelper(root, sum);
return root;
}
};

时间复杂度为\(O(n)\),即每个节点遍历一次。

今日总结

实际上是昨日总结...因为本来该4.15发的打卡到了4.16才发,原因是昨天去看了DragonForce北京演出。

发现二叉树部分的题目暂时算是刷完了,总的来说感觉大部分题目用递归来解决都并不算难,关键在于说要去往前、中、后序遍历的方向去套。以及递归中需要分情况讨论的地方要把所有可能性都考虑到,那么就基本都能做出来。

同时还有一个非常重要的点在于,在层序遍历中使用队列来解决问题的范式模板,注意当需要分层解决问题的时候,需要固定每一次循环开始时的队列长度,用于表示当前层的节点个数。

二叉树最难的地方感觉就在于,使用迭代来求解的时候,对于前序遍历和中序遍历的迭代法需要充分掌握。还有就是当遇到需要自下而上的算法的时候,需要使用回溯的方式。对于回溯算法,如果使用递归来实现,使用值传递可以免去一次pop出栈操作,但是值传递可能会导致每一次递归都复制一个栈,当数据量较大时会占用很大的空间。如果使用引用的方式进行回溯,则每次递归之后要手动使用pop进行回溯。

大致上总结了一下最近学到的东西,以及记录当前力扣进度:

目前力扣已经79题了,100题尽在眼前,继续加油

代码随想录第二十一天 | Leecode 669. 修剪二叉搜索树、108. 将有序数组转换为二叉搜索树、538. 把二叉搜索树转换为累加树的更多相关文章

  1. 代码随想录算法训练营day23 | leetcode 669. 修剪二叉搜索树 ● 108.将有序数组转换为二叉搜索树 ● 538.把二叉搜索树转换为累加树

    LeetCode 669. 修剪二叉搜索树 分析1.0 递归遍历树时删除符合条件(不在区间中)的节点-如何遍历如何删除 如果当前节点大于范围,递归左树,反之右树 当前节点不在范围内,删除它,把它的子树 ...

  2. LeetCode 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树

    第108题 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定有序数组: [-10 ...

  3. [LeetCode] 108. 将有序数组转换为二叉搜索树

    题目链接 : https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/ 题目描述: 将一个按照升序排列的 ...

  4. [LeetCode]105. 从前序与中序遍历序列构造二叉树(递归)、108. 将有序数组转换为二叉搜索树(递归、二分)

    题目 05. 从前序与中序遍历序列构造二叉树 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 题解 使用HashMap记录当前子树根节点在中序遍历中的位置,方便每次 ...

  5. LeetCode 108. 将有序数组转换为二叉搜索树(Convert Sorted Array to Binary Search Tree) 14

    108. 将有序数组转换为二叉搜索树 108. Convert Sorted Array to Binary Search Tree 题目描述 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索 ...

  6. Java实现 LeetCode 108 将有序数组转换为二叉搜索树

    108. 将有序数组转换为二叉搜索树 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: ...

  7. LeetCode【108. 将有序数组转换为二叉搜索树】

    又是二叉树,最开始都忘记了二叉搜索树是什么意思,搜索了一下: 二叉搜索树:左节点都小于右节点,在这里就可以考虑将数组中的中间值作为根节点 平衡二叉树:就是左右节点高度不大于1 树就可以想到递归与迭代, ...

  8. LeetCode 108——将有序数组转化为二叉搜索树

    1. 题目 2. 解答 一棵高度平衡的二叉搜索树意味着根节点的左右子树包含相同数量的节点,也就是根节点为有序数组的中值. 因此,我们将数组的中值作为根节点,然后再递归分别得到左半部分数据转化的左子树和 ...

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

    原题链接 思路: 二叉搜索树的定义: 它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的 ...

  10. leecode第四题(寻找两个有序数组的中位数)

    题解: class Solution { public: double findMedianSortedArrays(vector<int>& nums1, vector<i ...

随机推荐

  1. JAVA基础环境配置指南(简洁版)

    1.安装JDK 官网下载后直接安装 配置环境变量: 添加 JAVA_HOME 变量名:JAVA_HOME 变量值:C:\Program Files (x86)\Java\jdk1.8.0_91 // ...

  2. SpringBoot实现HandlerInterceptor拦截器的接口没有需要重写的方法也不报错是怎么回事

    以前实现HandlerInterceptor接口,总会提示需要实现3个方法(preHandle.postHandle.afterCompletion).现在没有出现提示.原因:这是Java8的新特性- ...

  3. C# List应用 Lambda 表达式

    参考链接 : https://blog.csdn.net/wori/article/details/113144580 首先 => 翻译为{ } 然后没有然后 主要基于我工作中常用的几种情况,写 ...

  4. gitlab - [02] 安装部署

    安装部署篇 一.5分钟搭建私人代码仓库 (1)设置环境变量:export GITLAB_HOME=/src/gitlab (2)编写docker-compose.yml mkdir -p /opt/d ...

  5. Visual Studio 卸载

    1.找个安装镜像文件 2.必须以管理员身份运行cmd 3.在CMD里输入"G:\vs_professional.exe /uninstall /force" 4.企业版就把prof ...

  6. 关于我在使用Steamlit中碰到的问题及解决方案总结

    Steamlit 并不支持一个可以预览本地文件的路径选择器(并不上传文件) 解决方案:使用 Python 自带的 tkinter 来完成 参考:[Streamlit 选择文件夹的曲折方案]Stream ...

  7. FastAPI 路径参数完全指南:从基础到高级校验实战 🚀

    title: FastAPI 路径参数完全指南:从基础到高级校验实战 date: 2025/3/5 updated: 2025/3/5 author: cmdragon excerpt: 探讨 Fas ...

  8. 【P4】Verilog搭建单周期MIPS-CPU

    课下 Bug_Log 1.模块实例化的信号需先定义,且记得定义完备 其实testbench见过多次了,自己写的时候还想不清. 若实例化模块时使用的信号,若事先无声明,则会自动生成1bit此名称信号,自 ...

  9. mysql8导入myslq5 报错

    打开sql文件替换 我的数据库编码是utf8mb4,如果你的数据库编码是别的,替换成你自己的编码. utf8mb4_0900_ai_ci替换为utf8mb4_general_ci

  10. Oracle配置和性能优化方法

          性能是衡量软件系统的一个重要部分,可能引起性能低下的原因很多,如CPU/内存/网络资源不足,硬盘读写速度慢,数据库配置不合理,数据库对象规划或存储方式不合理,模块设计对性能考虑不足等. 1 ...