代码随想录-Day23
669. 修剪二叉搜索树
方法一:递归
class Solution {public TreeNode trimBST(TreeNode root, int low, int high) {if (root == null) {return null;}if (root.val < low) {return trimBST(root.right, low, high);} else if (root.val > high) {return trimBST(root.left, low, high);} else {root.left = trimBST(root.left, low, high);root.right = trimBST(root.right, low, high);return root;}}
}
这段代码定义了一个名为 Solution
的类,其中包含一个方法 trimBST
,用于修剪(裁剪)给定二叉搜索树(BST)中的节点,使得所有节点的值在指定的区间 [low, high]
内。修剪操作应当保持二叉搜索树的性质。以下是代码逻辑的详细解析:
-
基本情况处理:首先检查根节点
root
是否为空。如果为空,直接返回null
,因为没有节点需要修剪。 -
节点值处理:
- 如果当前节点
root
的值root.val
小于low
,说明当前节点及其左子树都不可能在保留范围内,因此直接递归地对右子树root.right
调用trimBST
方法,并返回结果作为新的根节点。这一步相当于“跳过”当前节点及其左子树。 - 如果当前节点
root
的值root.val
大于high
,说明当前节点及其右子树都不可能在保留范围内,因此直接递归地对左子树root.left
调用trimBST
方法,并返回结果作为新的根节点。这一步相当于“跳过”当前节点及其右子树。 - 如果当前节点的值满足
low <= root.val <= high
,说明当前节点值在指定区间内,需要保留。此时,递归地对左右子树进行修剪,并保持当前节点作为修剪后子树的根节点。
- 如果当前节点
-
递归修剪子树:当当前节点值符合条件需要保留时,分别对左子树
root.left
和右子树root.right
递归调用trimBST
方法,以确保整个子树都被正确修剪。 -
返回处理后的节点:经过上述处理后,直接返回当前处理过的节点
root
,作为修剪后子树的根。递归过程中,这个返回值会被上一层调用用来构建整个修剪后的BST。
通过这样的递归逻辑,trimBST
方法能够从根节点开始,逐步构建出只包含值在 [low, high]
范围内的二叉搜索树,并保持BST的性质。不在范围内的节点及其子树都会被“剪掉”,从而实现高效的修剪操作。
方法二:迭代
class Solution {public TreeNode trimBST(TreeNode root, int low, int high) {while (root != null && (root.val < low || root.val > high)) {if (root.val < low) {root = root.right;} else {root = root.left;}}if (root == null) {return null;}for (TreeNode node = root; node.left != null; ) {if (node.left.val < low) {node.left = node.left.right;} else {node = node.left;}}for (TreeNode node = root; node.right != null; ) {if (node.right.val > high) {node.right = node.right.left;} else {node = node.right;}}return root;}
}
这段代码提供了另一种实现方式,使用迭代方法来修剪二叉搜索树(BST),使其所有节点的值落在指定区间 [low, high]
内。相较于递归方法,迭代方法直接利用循环进行遍历和修剪。以下是代码逻辑的详细解析:
-
初始化:首先,代码通过一个
while
循环找到BST的第一个(最左边的)落在指定区间[low, high]
内的节点作为新的根节点。如果根节点root
的值小于low
,则向右移动(因为BST的性质保证了所有左子节点的值都小于根节点,所以要找大于等于low
的节点,只能往右走);如果根节点的值大于high
,则向左移动。如果整个树的所有节点都不在区间内,最终root
会变成null
,直接返回null
。 -
修剪左子树:接下来,使用一个
for
循环来修剪根节点的左子树。循环的条件是当前节点的左子节点不为空。如果左子节点的值小于low
,说明整个左子树都不在指定区间内,直接将当前节点的左子节点更新为其左子节点的右子节点(跳过整个左子树的左部分);否则,将当前节点更新为其左子节点,继续检查更左的节点。 -
修剪右子树:随后,再使用一个类似的
for
循环来修剪根节点的右子树。循环的条件是当前节点的右子节点不为空。如果右子节点的值大于high
,说明整个右子树的右部分都不在指定区间内,直接将当前节点的右子节点更新为其右子节点的左子节点(跳过整个右子树的右部分);否则,将当前节点更新为其右子节点,继续检查更右的节点。 -
返回修剪后的根节点:经过上述两步修剪后,根节点及其左右子树都已经满足条件,直接返回
root
作为修剪后的二叉搜索树的根。
这种迭代方法同样保持了BST的性质,并且在处理每个节点时都是常量时间复杂度,总体时间复杂度为O(N),其中N为树中的节点数,空间复杂度为O(1),因为它只使用了固定数量的指针变量。
108. 将有序数组转换为二叉搜索树
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵
平衡二叉搜索树。
方法一:中序遍历,总是选择中间位置左边的数字作为根节点
class Solution {public TreeNode sortedArrayToBST(int[] nums) {return helper(nums, 0, nums.length - 1);}public TreeNode helper(int[] nums, int left, int right) {if (left > right) {return null;}// 总是选择中间位置左边的数字作为根节点int mid = (left + right) / 2;TreeNode root = new TreeNode(nums[mid]);root.left = helper(nums, left, mid - 1);root.right = helper(nums, mid + 1, right);return root;}
}
这段代码定义了一个名为 Solution
的类,其中包含两个方法,用于将一个有序数组(升序)转换成一棵高度平衡的二叉搜索树(BST)。二叉搜索树的特性是左子树所有节点的值小于根节点的值,右子树所有节点的值大于根节点的值,且每个节点的左、右子树也分别是BST。下面是代码的详细解析:
-
sortedArrayToBST(int[] nums)
方法:这是主要的接口方法,接收一个有序数组nums
作为参数,然后调用辅助方法helper
来完成转换工作,最终返回构建好的BST的根节点。 -
helper(int[] nums, int left, int right)
方法:这是一个递归辅助方法,用于实际构建BST。- 输入参数:
nums
是原始有序数组,left
和right
分别表示当前子数组的左右边界索引。 - 终止条件:如果
left > right
,表示当前子数组为空,没有节点可构建,因此返回null
。 - 选择根节点:为了构建高度平衡的BST,总是选择中间位置(或中间偏左,这里取中间下标
mid = (left + right) / 2
)的元素作为根节点的值,这样做可以保证树尽量平衡。注意,当left
和right
为偶数时,mid
实际上取的是中间两个数中左边的那个。 - 递归构建左右子树:以
mid
为界,分别对左半部分[left, mid - 1]
和右半部分[mid + 1, right]
递归调用helper
方法,构建当前节点的左子树和右子树。 - 返回根节点:构建好左右子树后,返回当前子树的根节点。
- 输入参数:
通过这样的递归划分,每个子数组都会被处理成一个高度平衡的BST子树,最终整个数组转换成了一棵高度平衡的二叉搜索树。这种方法充分利用了有序数组的特性,保证了构建的BST不仅是正确的,而且高度平衡,提高了树的查询效率。
方法二:中序遍历,总是选择中间位置右边的数字作为根节点
class Solution {public TreeNode sortedArrayToBST(int[] nums) {return helper(nums, 0, nums.length - 1);}public TreeNode helper(int[] nums, int left, int right) {if (left > right) {return null;}// 总是选择中间位置右边的数字作为根节点int mid = (left + right + 1) / 2;TreeNode root = new TreeNode(nums[mid]);root.left = helper(nums, left, mid - 1);root.right = helper(nums, mid + 1, right);return root;}
}
这段代码与之前提供的解决方案非常相似,都是将一个有序数组(升序)转换为一棵高度平衡的二叉搜索树(BST)。主要区别在于选取中间元素的方式:之前的解决方案选取中间位置左边的数字作为根节点,而这里的代码选择的是中间位置右边的数字。下面是代码解析:
-
sortedArrayToBST(int[] nums)
方法:此方法作为接口,接收一个有序数组nums
,然后调用辅助方法helper
来构建平衡BST,并返回根节点。 -
helper(int[] nums, int left, int right)
方法:这是一个递归辅助方法,用于递归构建BST。- 输入参数:
nums
是原始有序数组,left
和right
分别表示当前考虑构建子树的数组范围。 - 终止条件:当
left > right
时,表示当前区间为空,无需构建节点,直接返回null
。 - 选择根节点:与之前版本不同,这里通过
(left + right + 1) / 2
计算中间索引,目的是选择区间的中间位置右边的数作为根节点。这确保了当数组长度为奇数时,中间值取右侧;偶数时,同样偏向取右侧的值作为根。 - 递归构建子树:基于选定的根节点值,分别对左半区间
[left, mid - 1]
和右半区间[mid + 1, right]
递归调用helper
方法,构建当前节点的左子树和右子树。 - 返回根节点:构建完左右子树后,返回当前子树的根节点。
- 输入参数:
通过这样的递归过程,整个数组被均衡地分割并构建为一棵高度平衡的BST,其中每个节点的值都来自数组中的一个位置,且树保持了BST的性质(左子树所有节点值小于根节点值,右子树所有节点值大于根节点值)。选择中间偏右的元素作为根节点是实现平衡的一种方式,虽不如选取正中间那样绝对平衡,但在大多数情况下能保持较好的平衡性。
方法三:中序遍历,选择任意一个中间位置数字作为根节点
class Solution {Random rand = new Random();public TreeNode sortedArrayToBST(int[] nums) {return helper(nums, 0, nums.length - 1);}public TreeNode helper(int[] nums, int left, int right) {if (left > right) {return null;}// 选择任意一个中间位置数字作为根节点int mid = (left + right + rand.nextInt(2)) / 2;TreeNode root = new TreeNode(nums[mid]);root.left = helper(nums, left, mid - 1);root.right = helper(nums, mid + 1, right);return root;}
}
这段代码依然致力于将一个有序数组(升序)转换为一棵高度平衡的二叉搜索树(BST),但与之前的实现有所不同,它在选择中间元素作为根节点时引入了随机性,以提高算法在特定输入下的性能表现。下面是代码的详细解析:
-
类成员变量:定义了一个
Random
类型的成员变量rand
,用于生成随机数。 -
sortedArrayToBST(int[] nums)
方法:此方法与之前的实现相同,作为对外接口,接收有序数组并调用helper
方法构建BST。 -
helper(int[] nums, int left, int right)
方法:这个递归辅助方法负责实际的转换工作,其参数含义也保持一致。不同之处在于如何选择中间元素:- 随机选择中间位置:这里使用公式
(left + right + rand.nextInt(2)) / 2
来确定中间位置的索引。rand.nextInt(2)
会返回0或1,加上left
和right
后除以2,实质上会在左侧的中间位置和右侧的中间位置之间随机选择一个索引作为根节点。这种方法在理论上可以避免在特定输入数据下构造出极端不平衡的BST,比如当有序数组本身就是近乎有序的情况下,传统的总是选择中间位置作为根节点的方法可能导致构造出来的BST极度倾斜。通过随机化选择,可以使得构造的BST在统计学意义上更加平衡,提高树的操作效率,如查找、插入等。
- 随机选择中间位置:这里使用公式
-
构建左右子树:与之前的实现相同,根据选定的中间位置索引,递归地构建当前节点的左子树和右子树。
通过这种方式,尽管每次运行时可能生成不同的BST(因为根节点的选择是随机的),但整体上仍然能保证构建出的BST高度平衡,同时在一定程度上优化了在特定输入下的性能表现,特别是对于那些可能导致递归方法偏向构建非平衡树的特殊排序数组。
538. 把二叉搜索树转换为累加树
反序中序遍历
class Solution {int sum = 0;public TreeNode convertBST(TreeNode root) {if (root != null) {convertBST(root.right);sum += root.val;root.val = sum;convertBST(root.left);}return root;}
}
这段代码定义了一个名为 Solution
的类,其中包含一个方法 convertBST
,该方法旨在将一个二叉搜索树(BST)转换成一个累加树。累加树是一种特殊的二叉树,其中每个节点的值等于原来的节点值加上所有大于它的节点值(在原BST中)。具体实现细节如下:
-
类成员变量:
sum
:这是一个整型成员变量,初始化为0,用于在遍历过程中累加节点值。
-
convertBST(TreeNode root)
方法:- 输入参数:
root
是二叉搜索树的根节点。 - 返回值:返回转换后的二叉搜索树的根节点,现在它变成了一棵累加树。
- 输入参数:
方法内部逻辑遵循后序遍历的顺序(右子树 -> 根节点 -> 左子树),这是解决此类问题的关键,原因如下:
- 先遍历右子树:由于BST的性质(左子树的所有节点小于根节点,右子树的所有节点大于根节点),先访问右子树意味着我们从最大的节点开始累加,这符合累加树的要求。
- 累加当前节点值:在访问完当前节点的右子树后,将当前节点的值与已累加的值相加,然后更新当前节点的值。
- 遍历左子树:最后遍历左子树,这样在访问左子树的每个节点时,它们都会得到已经更新过的父节点及父节点右边所有节点的累加和。
通过这样的递归过程,整个BST被转换成了累加树,且每个节点的值都正确反映了累加的规则。最后,该方法返回转换后的树的根节点。
相关文章:

代码随想录-Day23
669. 修剪二叉搜索树 方法一:递归 class Solution {public TreeNode trimBST(TreeNode root, int low, int high) {if (root null) {return null;}if (root.val < low) {return trimBST(root.right, low, high);} else if (root.val > high) {return trimBS…...

基于Visual Studio版本的AI编程助手
Visual Studio 是一个出色的 IDE,可用于构建适用于 Windows、Mac、Linux、iOS 和 Android 的丰富、精美的跨平台应用程序。 使用一系列技术(例如 WinForms、WPF、WinUI、MAUI 或 Xamarin)构建丰富。 1、安装 点击上方工具栏拓展选项,选择管理拓展选项 接着在联机页面中搜索&q…...

04-Vue:ref获取页面节点--很简单
目录 前言在Vue中,通过 ref 属性获取DOM元素使用 ref 属性获取整个子组件(父组件调用子组件的方法) 前言 我们接着上一篇文章 03-02-Vue组件之间的传值 来讲。 下一篇文章 05-Vue路由 在Vue中,通过 ref 属性获取DOM元素 我们当然…...

CBK-D2-安全与架构工程.md
CBK-D2-安全与架构工程 密码学和对称密钥算法 密码通信的基础知识 明文P-plaintext、加密encrypt、密文C-ciphertext、解密decrypt、密钥Key 多数情况下,密钥无非是一个极大的二进制数 每一种算法都有一个特定密钥控制key space,是一个特定的数值范围 密钥空间由位大小b…...

Windows驱动开发系列文章一
文章目录 环境搭建如何调试实时调试非实时调试 环境搭建 基本上按照官方网站安装 VisualStudio/SDK/WDK 这些软件就可以了 详情请参考这个安装链接 如何调试 Windows 调试分为两种:一种是实时调试,一种是非实时调试 实时调试 这个就需要用到Microso…...

java项目之人事系统源码(springboot+vue+mysql)
风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的人事系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 基于vue的人事系统的主要使用者…...

I/O '24|学习资源焕新,技术灵感升级
2024 年 5 月 15 日凌晨举行的 Google I/O 大会为各地的开发者们带来了新的灵感。面对技术革新,相信各位开发者们都迫不及待想要自己上手试一试。 别急,Google 谷歌今年为中国的开发者们准备了一份特别的学习资源,让开发者们自由探索新知。 G…...

前端应用开发实验:表单控件绑定
目录 实验目的相关知识点实验内容代码实现效果 实验目的 (1)熟练掌握应用v-model指令实现双向数据绑定的方法,学会使用 v-model指令绑定文本框、复选框、单选按钮、下拉菜单; (2)学会值绑定(将…...

[双指针] --- 快乐数 盛最多水的容器
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: 算法Journey 本篇博客我们分享一下双指针算法中的快慢指针以及对撞双指针,下面我们开始今天的学习吧~ 🏠 快乐数 📒 题…...

操作系统 - 输入/输出(I/O)管理
输入/输出(I/O)管理 考纲内容 I/O管理基础 设备:设备的基本概念,设备的分类,I/O接口 I/O控制方式:轮询方式,中断方式,DMA方式 I/O软件层次结构:中断处理程序,驱动程序,…...

代码随想录算法训练营第22天(py)| 二叉树 | 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树
669. 修剪二叉搜索树 力扣链接 给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>L) 思路 如果当前节点元素小于low,递归右子树,返回符合条件的头节点 如果当前节点元…...

使用C语言实现学生信息管理系统
前言 在我们实现学生信息管理系统的过程中,我们几乎会使用到C语言最常用最重要的知识,对于刚学习完C语言的同学来说是一次很好的巩固机会,其中还牵扯到数据结果中链表的插入和删除内容。 实现学生信息管理系统 文件的创建与使用 对于要实现…...

上下文视觉提示实现zero-shot分割检测及多visual-prompt改造
文章目录 一、Closed-Set VS Open-set二、DINOv2.1 论文和代码2.2 内容2.3 安装部署2.4 使用效果 三、多visual prompt 改造3.1 获取示例图mask3.2 修改函数参数3.3 推理代码3.4 效果的提升! 四、总结 本文主要介绍visual prompt模型DINOv,该模型可输入八…...

WebGL学习(一)渲染关系
学习webgl 开发理解渲染关系是必须的,也非常重要,很多人忽视了这个过程。 我这里先简单写一下,后面尽量用通俗易懂的方式,举例讲解。 WebGL,全称Web Graphics Library,是一种在网页上渲染3D图形的技术。它…...

人生建议:向猫学习
心安理得地被爱 猫从不担心自己不配得到爱,也正是这幅理所应当、宠辱不惊的样子,让人欲罢不能。或许 当你相信自己值得世界上最好的爱时,你就会拥有。 多晒太阳多睡觉 猫喜欢睡觉,尤其喜欢躺阳光好的地方。阳光和睡眠,…...

软件架构设计属性之三:结构性属性浅析
文章目录 引言一、结构性属性的定义二、结构性属性的关键要素1. 组件化2. 模块化3. 层次化4. 接口定义5. 数据流6. 依赖管理 三、结构性属性的设计原则1. 高内聚低耦合2. 松耦合3. 清晰的接口4. 可维护性5. 可扩展性 四、结构性属性的实现策略1. 组件划分2. 模块化设计3. 接口设…...

JAVA:多线程常见的面试题和答案
请关注微信公众号:拾荒的小海螺 博客地址:http://lsk-ww.cn/ 1、并发编程三要素? 原 子 性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行。可 见 性 可见性指多…...

短信平台-平台群发短信
时代的进步带来了我们生活的便利,而其中最受欢迎和广泛应用的方式之一就是通过短信传递信息。在这个飞速发展的数字时代,我们需要一个高效、可靠的短信平台来满足不断增长的通讯需求。而今天,我要向大家推荐的正是这样一款卓越的短信平台——…...

C++:类和对象
一、前言 C是面向对象的语言,本文将通过上、中、下三大部分,带你深入了解类与对象。 目录 一、前言 二、部分:上 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 4.类的访问限定符及封装 5.类的作用域 6.类的实例化 7.类的…...

JavaScript条件语句与逻辑判断:解锁代码逻辑的奥秘【含代码示例】
JavaScript条件语句与逻辑判断:解锁代码逻辑的奥秘【含代码示例】 基本概念与作用if...else:决策的基础switch:多路分支的能手逻辑运算符:连接逻辑的纽带三元运算符:简洁的力量 功能使用思路与技巧短路求值优化防止swi…...

sparksql自定义函数
前言 Spark SQL UDF(也称为用户定义函数)是Spark SQL&DataFrame最有用的功能,它扩展了Spark内置功能。在本文中,我将解释什么是UDF?为什么我们需要它,以及如何使用Java、Scala示例在DataFrame和SQL上创建和使用它。 注意:UDF是最昂贵的操作,因此只有在必要时才使用…...

新人开发新系统,旧人维护旧系统
通常来说旧系统存在一些难以解决的问题,软件架构及逻辑实现可能会有一定的缺陷和复杂度,甚至有些烂系统可以称为”焦油坑“,意思是出现问题难以分析解决,谁来谁陷进去。因此,如果同时存在新系统(可能正在开…...

鸿蒙应用模型:【Stage模型开发】概述
Stage模型开发概述 基本概念 下图展示了Stage模型中的基本概念。 图1 Stage模型概念图 [AbilityStage] 每个Entry类型或者Feature类型的HAP在运行期都有一个AbilityStage类实例,当HAP中的代码首次被加载到进程中的时候,系统会先创建AbilityStage实例…...

java使用jdbcTemplatep批量插入数据
JdbcTemplate 是 Spring 框架中提供的一个简化 JDBC 操作的工具类,它封装了 JDBC 的核心功能,使得开发者能够更方便、简洁地进行数据库操作。 下面是一个使用 JdbcTemplate 进行批量插入的示例: import org.springframework.jdbc.core.Batch…...

K8s service 进阶
文章目录 K8s service 进阶Service 工作逻辑Service 具体实现Service 资源类型ClusterIPNodePortLoadBalancerExternalName Service 与 EndpointEndpoint 与 容器探针自定义Endpoint Service 相关字段sessionAffinityexternalTrafficPolicyinternalTrafficPolicypublishNotRead…...

CompletableFuture详细讲解
目录 一、基本概念 1.1 异步编程 1.2 CompletableFuture简介 二、创建和完成CompletableFuture 2.1 创建CompletableFuture对象 2.2 手动完成CompletableFuture 2.3 异常完成CompletableFuture 三、异步计算和回调 3.1 异步任务的执行 3.2 处理计算结果 四、组合多个…...

【Linux】初识Linux和Linux环境配置
1.什么是Linux操作系统 说到电脑系统 我想有大多数人会脱口而出:windows、mac 是的,这也是如今市场上主流的两种操作系统。 但是对于IT相关的人士来说,还有一种系统也是必须有姓名 那就是Linux Linux,Linux Is Not UniX 的…...

redis-cli help使用
1. redis-cli命令使用—先连接上服务器 连接到 Redis 服务器: 使用 redis-cli 命令即可连接到本地运行的 Redis 服务器,默认连接到本地的 6379 端口。 redis-cli如果 Redis 服务器不在本地或者端口不同,可以使用 -h 和 -p 参数指定主机和端…...

中华活页文选高中版投稿发表
《中华活页文选(高中版)》创刊于1960年,是中宣部所属中国出版传媒股份有限公司主管、中华书局主办的国家级基础教育期刊,曾获得“中国期刊方阵双效期刊”、国家新闻出版广电总局推荐的“百种优秀报刊”等荣誉称号。本刊以高中学科…...

[图解]企业应用架构模式2024新译本讲解02-表数据入口
1 00:00:00,420 --> 00:00:04,330 这个案例,我们就是用书上的案例了 2 00:00:06,080 --> 00:00:08,860 收入确认的一个案例 3 00:00:09,510 --> 00:00:11,100 书上讲了,收入确认 4 00:00:13,330 --> 00:00:15,270 就是说,你给…...