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

专题归类: 05-二叉树 LeetCode 链接: https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/


在线做题: 写 Python 代码并运行 · Java 跳转 LeetCode

题目描述

给你一个整数数组 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] 都是高度平衡的 BST。

提示:

  • 1 <= nums.length <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • nums严格递增 顺序排列

题目详细分析

  • 数据范围含义: 最长 10000 个元素,值范围 [-10000, 10000],递归深度约 log₂(10000) ≈ 14,完全不用担心栈溢出。因为每次取中点分割数组,树高天然为 O(log n)。
  • 输入输出特征: 输入是升序整数数组,输出是树的根节点。数组是严格递增的(无重复值)。
  • 边界条件: nums 至少有一个元素(题目保证长度 ≥ 1)。
  • 核心约束: 必须构建 高度平衡 的 BST。这意味着不能简单地把数组串成链状,而要用二分的方式构建。
  • 隐藏条件: 只要平衡即可,答案可能不唯一(因为中点取法可能左偏或右偏)。

小白版直白理解

就像搭人梯拍照——要让最高的站在中间,个子矮的往两边依次排开,这样才能让整个队伍两边高度尽量平衡。有序数组的中间值就是树的根,左边的(更小的)全去左子树,右边的(更大的)全去右子树,左右子树用同样的方式继续搭建。


解题思路

思路一:递归取中点(推荐)

核心思想: 每次取数组的中间元素作为当前子树的根,左半部分递归构建左子树,右半部分递归构建右子树。由于每次都取中点,左右子树的元素数量最多相差 1,树的高度自然平衡。

为什么这样就能保证平衡? 因为每层递归都将数组对半分割,左右子树的节点数最多差 1,递归深度为 O(log n)。每个节点的左右子树大小基本相等,自然平衡。

def sortedArrayToBST(nums):
    def build(left, right):
        # 终止条件:区间为空
        if left > right:
            return None
 
        # 取中点作为根节点
        mid = (left + right) // 2
        root = TreeNode(nums[mid])
 
        # 递归构建左右子树
        root.left = build(left, mid - 1)
        root.right = build(mid + 1, right)
 
        return root
 
    return build(0, len(nums) - 1)

思路二:迭代法(栈模拟)

用栈模拟递归过程,避免系统递归调用。适合对递归深度有严格限制的场景。

def sortedArrayToBST(nums):
    if not nums:
        return None
 
    # 栈中存储 (节点, 左边界, 右边界, 是否已处理左右)
    # 使用一个虚拟根节点
    mid = (0 + len(nums) - 1) // 2
    root = TreeNode(nums[mid])
    stack = [(root, 0, len(nums) - 1, False)]
 
    while stack:
        node, l, r, processed = stack.pop()
        mid = (l + r) // 2
 
        if not processed:
            # 第一次处理:先入栈(后序处理),再处理左右
            stack.append((node, l, r, True))
            # 处理左子树
            if l <= mid - 1:
                left_mid = (l + mid - 1) // 2
                node.left = TreeNode(nums[left_mid])
                stack.append((node.left, l, mid - 1, False))
            # 处理右子树
            if mid + 1 <= r:
                right_mid = (mid + 1 + r) // 2
                node.right = TreeNode(nums[right_mid])
                stack.append((node.right, mid + 1, r, False))
 
    return root

易错点

  • 终止条件是 left > right 而不是 left >= right 当 left == right 时,还有一个元素需要构建节点。> 才是区间为空。
  • 中点计算溢出问题: 在 Java/C++ 中 (left + right) // 2 可能溢出,更安全的写法是 left + (right - left) // 2。Python 中无此问题。
  • 严格递增数组,无需处理重复值: 题目保证严格递增,但如果是「非严格递增」,BST 的左右分配就需要考虑重复值策略。
  • 取整方向: (left + right) // 2 是向下取整,会偏左。如果总想偏右可以用 (left + right + 1) // 2

框架提炼

分治法 + 递归构建二叉搜索树模板:

def build(l, r):
    if l > r:
        return None
    mid = (l + r) // 2          # 取中点
    root = TreeNode(nums[mid])  # 构建根
    root.left = build(l, mid - 1)   # 左半部分
    root.right = build(mid + 1, r)  # 右半部分
    return root

这个「取中点、分左右」的模式是「有序数组/链表 → 平衡 BST」的标准解法,也适用于将有序链表转换为 BST(只是找中点的方式变为快慢指针)。


关联题目