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

频次 ★★★★ · 难度 🟢 · 高频:字节/美团

题目

将一个升序数组转换为一棵高度平衡的 BST:任意节点左右子树高度差不超过 1。

示例

输入: [-10, -3, 0, 5, 9]
输出:     0
        /   \
      -3     9
      /     /
    -10    5
(或 [0,-3,9,-10,null,5] 等其他高度平衡 BST)

思路

二分递归:升序数组天然满足 BST 中序遍历结果。取中间元素为根,左边子数组递归构造左子树,右边子数组递归构造右子树。

取中间元素时,mid = left + (right - left) / 2(偏左)或 mid = (left + right + 1) / 2(偏右)都能得到高度平衡的 BST,答案不唯一。

代码

public TreeNode sortedArrayToBST(int[] nums) {
    return build(nums, 0, nums.length - 1);
}
 
private TreeNode build(int[] nums, int left, int right) {
    if (left > right) return null;
    int mid = left + (right - left) / 2;  // 取中间偏左
    TreeNode root = new TreeNode(nums[mid]);
    root.left = build(nums, left, mid - 1);
    root.right = build(nums, mid + 1, right);
    return root;
}

复杂度

  • 时间:O(n) —— 每个元素创建一个节点
  • 空间:O(log n) —— 平衡树的递归深度

边界条件

  • 空数组:返回 null
  • 单元素数组:返回单节点
  • 偶数个元素:取 left + (right - left) / 2 会偏左,取 (left + right + 1) / 2 会偏右,都合法
  • 数组长度很大:递归深度 O(log n) 不会栈溢出

变式

  • 109. 有序链表转换 BST:链表无法 O(1) 取中间元素,需要快慢指针找中点,或者中序遍历 + 全局指针
  • 105. 从前序与中序遍历构造二叉树:同样是递归分治建树,但输入是两种遍历序列
  • 110. 平衡二叉树:反过来判断一棵树是否高度平衡
  • 要求”最平衡”(左右节点数差 ≤ 1):mid = (left + right) / 2 即可保证
  • 转换为任意 BST(不要求平衡):取第一个元素为根,其余递归到右子树

易错点

  • 递归终止条件 left > right 而非 left == right——left == right 时还要创建单个节点
  • mid 计算用 left + (right - left) / 2 防溢出,不要用 (left + right) / 2
  • 左子树范围 [left, mid - 1],右子树范围 [mid + 1, right]——mid 作为根不参与子树递归
  • 题目要求高度平衡但不要求答案唯一——中间元素取左中位数或右中位数都能通过,别被示例输出带偏

面试追问

  • 为什么取中间元素能保证高度平衡? 二分后左右子数组长度差 ≤ 1,递归构造出的子树高度差也 ≤ 1。数学归纳法可证
  • 链表版(109)怎么做? 快慢指针找中点,O(n log n);或者中序遍历 + 全局指针模拟,O(n) 且不需要找中点
  • 数组有重复值怎么办? BST 要求左子树严格小于根,有重复值时通常把相等的一段归到同一侧(取左边第一个不等的位置作边界)。本题保证升序无重复,不需要处理,但面试问到要能说清取舍

关联题