【力扣100题】32.将有序数组转换为二叉搜索树

一、题目描述

给你一个整数数组 nums,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。

平衡二叉搜索树:左右子树高度差不超过 1 的二叉搜索树。

示例

示例 输入 输出
示例1 nums = [-10,-3,0,5,9] [0,-3,9,-10,null,5]
示例2 nums = [1,3] [3,1]
复制代码
示例1的转换过程:

数组:[-10, -3, 0, 5, 9]
      0           ← 中间元素作为根
    /   \
  -3     9       ← 左右两部分递归构建
  / \   / \
-10  #  #  5    ← # 表示 null

可选的平衡BST(根为中间元素):
    0               0
   / \             / \
  -3  9           -10  5
  /    \            \  /
-10     5           -3  9

提示

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

二、解题思路总览

方法 核心思想 时间复杂度 空间复杂度
递归 选取中间元素为根,构建平衡树 O(n) O(log n)

核心思想

  • 数组已排序,中间元素恰好是BST的根节点
  • 左半部分构建左子树,右半部分构建右子树
  • 递归处理,自然平衡

为什么选中间元素?

  • 根是中间元素,左右元素数量相等或差1
  • 左右子树高度差 ≤ 1,保证平衡
  • BST的中序遍历正好是原数组

三、完整代码

cpp 复制代码
class Solution {
public:
    TreeNode* traversal(vector<int>& nums, int begin, int end) {
        if (begin > end) return nullptr;           // 1. 递归终止:空区间

        int mid = (end + begin) / 2;              // 2. 取中间元素作为根
        TreeNode* root = new TreeNode(nums[mid]); // 3. 创建根节点

        root->left = traversal(nums, begin, mid - 1);   // 4. 递归构建左子树
        root->right = traversal(nums, mid + 1, end);    // 5. 递归构建右子树

        return root;                               // 6. 返回根节点
    }

    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return traversal(nums, 0, nums.size() - 1); // 7. 入口调用
    }
};

四、算法流程图(ASCII)

示例1的转换过程

复制代码
数组:[-10, -3, 0, 5, 9]
      0           ← 中间元素作为根
    /   \
  -3     9       ← 左右两部分递归构建
  / \   / \
-10  #  #  5    ← # 表示 null

递归展开过程:

traversal([-10,-3,0,5,9], 0, 4)
        │
        ├── mid = (0+4)/2 = 2 → nums[2] = 0
        │
        ├── traversal(..., 0, 1)  ← 左半部分
        │       │
        │       ├── mid = (0+1)/2 = 0 → nums[0] = -10
        │       │
        │       ├── left = traversal(..., 0, -1) → nullptr
        │       ├── right = traversal(..., 1, 1) → nullptr
        │       └── return TreeNode(-10)
        │
        ├── traversal(..., 3, 4)  ← 右半部分
        │       │
        │       ├── mid = (3+4)/2 = 3 → nums[3] = 5
        │       │
        │       ├── left = traversal(..., 3, 2) → nullptr
        │       ├── right = traversal(..., 4, 4) → nullptr
        │       └── return TreeNode(5)
        │
        └── return TreeNode(0)

树结构对比

复制代码
严格平衡的BST:

        0           高度 = 3
       / \
     -3   9        高度 = 2     高度 = 2
     /     \       高度 = 1     高度 = 1
   -10      5      高度 = 1     高度 = 1
            \
             (null)

每个节点的左右子树高度差 ≤ 1

五、逐行解析

cpp 复制代码
class Solution {
public:
    // ─────────────────────────────────────────
    // 递归函数:在 nums[begin...end] 区间构建BST
    // 返回构建好的树的根节点
    // ─────────────────────────────────────────
    TreeNode* traversal(vector<int>& nums, int begin, int end) {
        // ─────────────────────────────────────────
        // 第1步:递归终止条件
        // begin > end 表示空区间,返回 nullptr
        // ─────────────────────────────────────────
        if (begin > end) return nullptr;

        // ─────────────────────────────────────────
        // 第2步:取中间元素作为根节点
        // (begin + end) / 2 取下取整
        // 中间元素保证左右子树平衡
        // ─────────────────────────────────────────
        int mid = (end + begin) / 2;

        // ─────────────────────────────────────────
        // 第3步:创建根节点
        // 根节点值为中间元素
        // ─────────────────────────────────────────
        TreeNode* root = new TreeNode(nums[mid]);

        // ─────────────────────────────────────────
        // 第4步:递归构建左子树
        // 左半部分:[begin, mid-1]
        // ─────────────────────────────────────────
        root->left = traversal(nums, begin, mid - 1);

        // ─────────────────────────────────────────
        // 第5步:递归构建右子树
        // 右半部分:[mid+1, end]
        // ─────────────────────────────────────────
        root->right = traversal(nums, mid + 1, end);

        // ─────────────────────────────────────────
        // 第6步:返回根节点
        // 供父节点连接
        // ─────────────────────────────────────────
        return root;
    }

    // ─────────────────────────────────────────
    // 入口函数
    // ─────────────────────────────────────────
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        // 空数组直接返回 nullptr
        if (nums.empty()) return nullptr;

        // 从整个数组范围开始构建
        return traversal(nums, 0, nums.size() - 1);
    }
};

六、复杂度分析

时间复杂度

分析 复杂度
每个元素访问一次 O(n)

推导

  • n 个元素,每个元素作为根访问一次
  • 构建节点是 O(1)
  • 总共 n 个节点,所以 O(n)

空间复杂度

分析 复杂度
递归调用栈深度 = 树高 = log n O(log n)

推导

  • 每次递归区间减半,所以递归深度是 log n(以2为底)
  • 不计输出空间(新建的树节点),栈空间是 O(log n)

七、面试追问 FAQ

问题 回答
为什么中间元素作为根能保证平衡? 数组已排序,中间元素左右元素数量相等或差1,所以左右子树节点数相等或差1
可以选其他位置作为根吗? 可以,但必须是中间附近的元素,否则树会不平衡
如果数组有偶数个元素,选哪个中间? 选中间两个中的任意一个都可以,结果都是平衡BST
BST的中序遍历有什么特点? BST中序遍历结果是升序数组,正好是原数组
为什么不直接用二分查找的思路? 本题就是二分构建,BST的根就是二分的中点

八、相关题目

题目 难度 关键点
108. 将有序数组转换为二叉搜索树 简单 本题
109. 有序链表转换二叉搜索树 中等 链表版本,找中点
110. 平衡二叉树 简单 检查平衡性
1382. 将二叉搜索树变平衡 中等 重构平衡BST

九、总结

对比项 说明
代码行数 核心7行
时间复杂度 O(n)
空间复杂度 O(log n)
递归顺序 前序遍历(先根后左右)
核心技巧 中间元素作为根,保证平衡

核心公式

复制代码
根节点 = nums[(begin + end) / 2]
左子树 = nums[begin...mid-1]
右子树 = nums[mid+1...end]

平衡的本质

  • 数组已排序
  • 中间元素左边的都小于它,右边的都大于它
  • 选取中间元素,左右子树节点数自然相等或差1
  • 因此高度差 ≤ 1

相关推荐
郑州光合科技余经理1 小时前
代驾系统架构拆解:订单链路、权限组织与私有化源码交付
开发语言·后端·算法·架构·系统架构·uni-app·php
To_OC4 小时前
LC 35 搜索插入位置:二分查找谁都会,边界条件谁写谁懵
javascript·算法·leetcode
码哥DFS4 小时前
二叉树的直径
开发语言·javascript·算法
营养充电站7 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
人工智能·算法·docker·jupyter
技术小黑8 小时前
RNN算法实战系列05 | 天气预测
人工智能·rnn·算法
Ivanqhz10 小时前
php8 use-def链
算法·php
evans在进步11 小时前
LeetCode 189 轮转数组:三次反转原地解决,图解 Java 实现
java·算法·leetcode
歌_顿11 小时前
Drawing_To_Model
算法
龍德明宇11 小时前
AI不会疼-龍德明宇
人工智能·算法·大语言模型llm·负主体性·ai存在论