当前位置: 首页 > news >正文

[Leetcode] 0108. 将有序数组转换为二叉搜索树

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严格递增 顺序排列

解法

二叉搜索树的中序遍历是升序序列,题目给定的数组是按照升序排序的有序数组,因此可以确保数组是二叉搜索树的中序遍历序列。

给定二叉搜索树的中序遍历,是否可以唯一地确定二叉搜索树?答案是否定的。如果没有要求二叉搜索树的高度平衡,则任何一个数字都可以作为二叉搜索树的根节点,因此可能的二叉搜索树有多个。img

如果增加一个限制条件,即要求二叉搜索树的高度平衡,是否可以唯一地确定二叉搜索树?答案仍然是否定的。img

直观地看,我们可以选择中间数字作为二叉搜索树的根节点,这样分给左右子树的数字个数相同或只相差 1,可以使得树保持平衡。如果数组长度是奇数,则根节点的选择是唯一的,如果数组长度是偶数,则可以选择中间位置左边的数字作为根节点或者选择中间位置右边的数字作为根节点,选择不同的数字作为根节点则创建的平衡二叉搜索树也是不同的。

img

确定平衡二叉搜索树的根节点之后,其余的数字分别位于平衡二叉搜索树的左子树和右子树中,左子树和右子树分别也是平衡二叉搜索树,因此可以通过递归的方式创建平衡二叉搜索树。

递归的基准情形是平衡二叉搜索树不包含任何数字,此时平衡二叉搜索树为空。

在给定中序遍历序列数组的情况下,每一个子树中的数字在数组中一定是连续的,因此可以通过数组下标范围确定子树包含的数字,下标范围记为 \([\textit{left}, \textit{right}]\)。对于整个中序遍历序列,下标范围从 \(\textit{left}=0\)\(\textit{right}=\textit{nums}.\text{length}-1\)。当 \(\textit{left}>\textit{right}\) 时,平衡二叉搜索树为空。

以下三种方法中,方法一总是选择中间位置左边的数字作为根节点,方法二总是选择中间位置右边的数字作为根节点,方法三是方法一和方法二的结合,选择任意一个中间位置数字作为根节点。

方法一:中序遍历,总是选择中间位置左边的数字作为根节点

选择中间位置左边的数字作为根节点,则根节点的下标为 \(\textit{mid}=(\textit{left}+\textit{right})/2\),此处的除法为整数除法。

时间复杂度:\(O(n)\),其中 \(n\) 是数组的长度。每个数字只访问一次。
空间复杂度:\(O(\log n)\),其中 \(n\) 是数组的长度。空间复杂度不考虑返回值,因此空间复杂度主要取决于递归栈的深度,递归栈的深度是 \(O(\log n)\)

img

Python3

class Solution:def sortedArrayToBST(self, nums: List[int]) -> TreeNode:def helper(left, right):if left > right:return None# 总是选择中间位置左边的数字作为根节点mid = (left + right) // 2root = TreeNode(nums[mid])root.left = helper(left, mid - 1)root.right = helper(mid + 1, right)return rootreturn helper(0, len(nums) - 1)

C++

class Solution {
public:TreeNode* sortedArrayToBST(vector<int>& nums) {return helper(nums, 0, nums.size() - 1);}TreeNode* helper(vector<int>& nums, int left, int right) {if (left > right) {return nullptr;}// 总是选择中间位置左边的数字作为根节点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;}
};

方法二:中序遍历,总是选择中间位置右边的数字作为根节点

选择中间位置右边的数字作为根节点,则根节点的下标为 \(\textit{mid}=(\textit{left}+\textit{right}+1)/2\),此处的除法为整数除法。

时间复杂度:\(O(n)\),其中 \(n\) 是数组的长度。每个数字只访问一次。
空间复杂度:\(O(\log n)\),其中 \(n\) 是数组的长度。空间复杂度不考虑返回值,因此空间复杂度主要取决于递归栈的深度,递归栈的深度是 \(O(\log n)\)

img

Python3

class Solution:def sortedArrayToBST(self, nums: List[int]) -> TreeNode:def helper(left, right):if left > right:return None# 总是选择中间位置右边的数字作为根节点mid = (left + right + 1) // 2root = TreeNode(nums[mid])root.left = helper(left, mid - 1)root.right = helper(mid + 1, right)return rootreturn helper(0, len(nums) - 1)

C++

class Solution {
public:TreeNode* sortedArrayToBST(vector<int>& nums) {return helper(nums, 0, nums.size() - 1);}TreeNode* helper(vector<int>& nums, int left, int right) {if (left > right) {return nullptr;}// 总是选择中间位置右边的数字作为根节点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;}
};

方法三:中序遍历,选择任意一个中间位置数字作为根节点

选择任意一个中间位置数字作为根节点,则根节点的下标为 \(\textit{mid}=(\textit{left}+\textit{right})/2\)\(\textit{mid}=(\textit{left}+\textit{right}+1)/2\) 两者中随机选择一个,此处的除法为整数除法。

时间复杂度:\(O(n)\),其中 \(n\) 是数组的长度。每个数字只访问一次。
空间复杂度:\(O(\log n)\),其中 \(n\) 是数组的长度。空间复杂度不考虑返回值,因此空间复杂度主要取决于递归栈的深度,递归栈的深度是 \(O(\log n)\)

img

Python3

class Solution:def sortedArrayToBST(self, nums: List[int]) -> TreeNode:def helper(left, right):if left > right:return None# 选择任意一个中间位置数字作为根节点mid = (left + right + randint(0, 1)) // 2root = TreeNode(nums[mid])root.left = helper(left, mid - 1)root.right = helper(mid + 1, right)return rootreturn helper(0, len(nums) - 1)

C++

class Solution {
public:TreeNode* sortedArrayToBST(vector<int>& nums) {return helper(nums, 0, nums.size() - 1);}TreeNode* helper(vector<int>& nums, int left, int right) {if (left > right) {return nullptr;}// 选择任意一个中间位置数字作为根节点int mid = (left + right + rand() % 2) / 2;TreeNode* root = new TreeNode(nums[mid]);root->left = helper(nums, left, mid - 1);root->right = helper(nums, mid + 1, right);return root;}
};
http://www.lryc.cn/news/212552.html

相关文章:

  • Pandas数据导入和导出:CSV、Excel、MySQL、JSON
  • 第16期 | GPTSecurity周报
  • 省钱兄短剧短视频视频滑动播放模块源码支持微信小程序h5安卓IOS
  • SDRAM学习笔记(MT48LC16M16A2,w9812g6kh)
  • ARM 学习笔记3 STM32G4 定时器相关资料整理
  • LeetCode 917 仅仅反转字母 简单
  • JAVA深化篇_25—— IO流章节全网最全总结(附详细思维导图)
  • 易基因:ChIP-seq等揭示BRWD3调控KDM5活性以维持H3K4甲基化水平的表观机制|PNAS
  • C++深度优先(DFS)算法的应用:收集所有金币可获得的最大积分
  • uniapp中APP端使用echarts用formatter设置y轴保留2位小数点不生效
  • 无糖茶饮三十年,从无人问津到人手一瓶
  • 面向Three.js开发者的3D自动纹理化开发包
  • 数字孪生技术与VR:创造数字未来
  • 系统架构设计师-第15章-面向服务架构设计理论与实践-软考学习笔记
  • 为什么我觉得Rust比C++复杂得多?
  • python sqlalchemy(ORM)- 03 增删改查
  • Flutter笔记:完全基于Flutter绘图技术绘制一个精美的Dash图标(上)
  • 学习gorm:彻底弄懂Find、Take、First和Last函数的区别
  • 796. 子矩阵的和(二维前缀和)
  • 利用ChatGPT进行股票走势分析
  • 万字解析设计模式之单例模式
  • vue2.x 二次封装element ui 中的el-dialog
  • ssh连接Ubuntu虚拟机出现connection reset by ip地址 port 22怎么解决
  • 树莓派4B安装ffmpeg
  • LeetCode|动态规划|1035. 不相交的线 、53. 最大子数组和
  • 一体式IO模块:汽车行业的数字化转型助推器
  • OpenCV官方教程中文版 —— Hough 直线变换
  • 【Axure高保真原型】百分比堆叠柱状图
  • Vue.js中的双向数据绑定(two-way data binding)
  • TFN 2.5G SDH传输分析仪 FT100-D300S