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

二叉树题解——将有序数组转换为二叉搜索树【LeetCode】优化解法

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

方法一、索引递归版

一、算法逻辑(逐步通顺讲解每一步思路)

该题要求将一个 升序整数数组 nums 转换为一棵高度平衡的二叉搜索树(BST),并返回其根节点。

✅ 1️⃣ 递归构建树的入口函数
调用 dfs(0, len(nums)) 处理整个数组范围,即从头到尾递归构造树结构。

✅ 2️⃣ 设定递归边界
dfs(left, right) 表示要将 nums[left:right] 这段数组构造成一棵子树。
left == right 时,子数组为空,没有节点可构建,直接返回 None

✅ 3️⃣ 选择中点作为当前子树的根
使用 mid = (left + right) // 2 取当前范围的中间元素作为根节点的值 nums[mid]。这样可以保持树尽可能平衡(左右子树大小接近)。

✅ 4️⃣ 递归构建左右子树

  • 左子树递归调用 dfs(left, mid):表示中点左边的元素形成左子树;

  • 右子树递归调用 dfs(mid + 1, right):表示中点右边的元素形成右子树。

✅ 5️⃣ 构造当前节点并拼接子树
每一层递归中,创建一个新的 TreeNode 节点,将左右递归结果接入后返回,即完成当前子树构建。

这样从上而下、从中间向两边地递归构造,最终形成了一棵平衡的二叉搜索树。


二、核心点总结

该算法的核心是:

分治 + 递归思想,选中点为根节点,递归构建左右子树,保持树的高度平衡。

✅ 使用数组中点作为当前子树的根节点,有效保证平衡性;
✅ 不断分割区间构建左右子树,天然匹配树的结构;
✅ 用索引代替切片,避免多余内存开销;
✅ 是将“数组 → 树结构”的典型构造模板。

class Solution:def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:def dfs(left:int, right:int) -> Optional[TreeNode]:if left == right:return Nonemid = (left + right)//2return TreeNode(nums[mid], dfs(left, mid), dfs(mid+1, right))return dfs(0, len(nums))

三、时间复杂度分析

每个元素都被访问一次,并且被构造成一个 TreeNode

  • 每次递归构造一个节点,总共构造 n 个节点;

  • 虽然递归树有深度 log n,但每一层的处理是分摊进行的,不重复处理元素。

时间复杂度:O(n)


四、空间复杂度分析

主要空间开销来源是 递归调用栈

  • 因为递归是平衡地分裂,最多递归 log n 层(等价于树的高度);

  • 不使用额外数组或辅助结构,空间占用主要来自递归本身。

空间复杂度:O(log n)


✅ 总结一句话

本算法通过分治递归+中点选根策略,在 O(n) 时间、O(log n) 空间内将有序数组构造成一棵平衡的 BST,是数组转树结构的经典构造技巧。

方法二、切片版

一、算法逻辑(逐步通顺讲解每一步思路)

该题要求将一个 升序排列的数组 nums 构造成一棵高度平衡的二叉搜索树(BST)

✅ 1️⃣ 递归出口判断
首先判断数组是否为空,if not nums:。为空时返回 None,表示当前子树为空,是递归的结束条件。

✅ 2️⃣ 选择中间元素作为当前子树的根节点
使用 mid = len(nums) // 2,取当前数组的中间索引;
nums[mid] 作为当前子树的根节点的值,确保左右子树元素数量尽可能接近。

✅ 3️⃣ 构建左子树和右子树

  • 左子树通过递归调用 self.sortedArrayToBST(nums[:mid]) 构造,传入左半部分;

  • 右子树通过 self.sortedArrayToBST(nums[mid+1:]) 构造,传入右半部分;

这两个递归调用分别对应中点两侧的子数组,继续重复当前的构造逻辑。

✅ 4️⃣ 返回当前根节点
使用 TreeNode(nums[mid], left, right) 创建当前节点,并接上递归得到的左右子树,形成当前子树的结构并返回。

这种方式从上到下递归、从中间向两端扩展,最终构建出一棵平衡的 BST。


二、核心点总结

该算法的核心是:

利用递归 + 数组切片 + 中点选根策略,构造高度平衡的 BST。

✅ 选中点作为根节点,有助于保证左右子树平衡;
✅ 使用递归天然符合“树结构的自相似”构造方式;
✅ 切片语法使代码简洁易读,但也带来了额外空间开销;
✅ 是经典的“分治 + 中点做根 + 递归左右”的树构造模式。

class Solution:def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:if not nums:return Nonemid = len(nums) // 2left = self.sortedArrayToBST(nums[:mid])right = self.sortedArrayToBST(nums[mid + 1:])return TreeNode(nums[mid], left, right)

三、时间复杂度分析

设数组长度为 n,递归深度为 log n

但这段代码中使用了 nums[:mid]nums[mid+1:] 切片操作,而 Python 中切片操作的时间复杂度是 O(k),需要复制新数组。

  • 每层会生成两个切片,平均长度约为 n/2, n/4, ..., 总和为 O(n log n)

  • 虽然节点数总共是 n,但切片操作在每层都创建了新的数组副本。

因此该算法的时间复杂度为

O(n log n)(构建树的递归是 O(n),切片复制引入了额外的 log n 层开销)


四、空间复杂度分析

空间开销来源于两个方面:

  • 递归栈深度:最多递归 log n 层(因为是平衡二叉树);

  • 数组切片副本:每次递归都会创建新的子数组,总体空间开销累积为 O(n)

因此该算法的空间复杂度为

O(n)(包含递归栈 O(log n) 和切片数组副本 O(n)


✅ 总结一句话

本算法通过递归和数组切片构建平衡 BST,代码简洁直观,但切片带来的复制开销使得时间复杂度上升为 O(n log n),空间复杂度也为 O(n)。若想进一步优化效率,建议使用索引边界递归的方式。

对比:

指标切片版索引版(推荐)
✅ 代码简洁度较高(语义直观)稍繁琐(需传索引)
✅ 时间复杂度O(n log n)(切片复制)O(n)
✅ 空间复杂度O(n)(包含切片副本)O(log n)(递归栈)
✅ 实际运行速度明显较慢明显更快
✅ 是否原地操作否(创建新数组)是(基于原数组)

切片版易懂但低效,索引版稍繁但高效。若追求 LeetCode 提交性能或面试表现,应优先选择“索引边界递归法”。

http://www.dtcms.com/a/266294.html

相关文章:

  • 微软医疗AI诊断系统发布 多智能体协作实现疑难病例分析
  • 怎么处理[TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark
  • windows安装ELK
  • Nginx 的安装部署
  • Web后端开发(事务管理、AOP)
  • 倾斜摄影无人机飞行航线规划流程详解
  • 无人机Ku相控阵卫星通信系统技术说明
  • jQuery Mobile 安装使用教程
  • 高可扩展属性建模设计:架构师的全局思考与落地方案
  • 云原生AI研发体系建设路径
  • PaddleOCR独立服务:高效OCR一站式解决方案
  • 简述MCP的原理-AI时代的USB接口
  • 如何把一台手机的屏幕投到另一台手机上
  • Perforce QAC 与 Klocwork 重磅升级:质量突破+许可降本
  • 【VScode | 格式化文档】一文掌握VScode使用 clang-format 的文档格式化(C/C++)
  • 文心大模型及百度大模型内容安全平台齐获信通院大模型安全认证
  • 微信小程序如何实现再多个页面共享数据
  • 机器学习中的数学---常用距离计算方法详解
  • 通过 Ansys Discovery CFD 仿真探索电池冷板概念
  • 睿尔曼系列机器人——以创新驱动未来,重塑智能协作新生态(下)
  • 【IM项目笔记】1、WebSocket协议和服务端推送Web方案
  • 在 Ubuntu 22.04 上使用 Minikube 部署 Go 应用到 Kubernetes
  • 商品中心—19.库存分桶高并发的优化文档
  • element-plus按需自动导入的配置 以及icon图标不显示的问题解决
  • Ubuntu 22.04 + MySQL 8 无密码登录问题与 root 密码重置指南
  • ubuntu22桌面版中文输入法 fcitx5
  • goole chrome变更默认搜索引擎为百度
  • MySQL(116)如何监控负载均衡状态?
  • 如何调节笔记本电脑亮度?其实有很多种方式可以调整亮度
  • Linux中容器文件操作和数据卷使用以及目录挂载