【力扣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

相关推荐
CodeRecycle1 小时前
Python 自动配置 pip 支持库(通过 Windows Bat 脚本)
算法
liliangcsdn1 小时前
特质偏度因子的计算示例和分析
算法
荆棘鸟智能2 小时前
无人机遥感图像实时拼接算法工程实践:从特征提取到全景融合的完整技术链路
算法·无人机
xxwxx__2 小时前
C++ STL set 与 map 全套详解:关联式容器、红黑树底层、代码坑点、刷题实战
数据结构·c++
6Hzlia2 小时前
【Classic 150 刷题计划】 LeetCode 58. 最后一个单词的长度 | C++ 极简反向遍历与单变量状态机
算法
AgentMaster3 小时前
企业如何应用智能客服?5 个典型场景的技术架构与实施路径
大数据·算法
程曦曦3 小时前
MySQL 生产库误删 98 张表后的时间点恢复实战:从 binlog 解析到资金对账
linux·数据结构·其他·算法·ubuntu·运维开发
郝学胜-神的一滴3 小时前
Effective Python 条款 13:善用星号解包,告别下标切片拆分的坑
服务器·开发语言·数据结构·python·程序人生·pycharm
Logic1013 小时前
C语言/数据结构位运算题解:异或XOR找出流水线上的“独特零件编号“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
weixin_307779134 小时前
C++代码实现MATLAB中的ode45函数功能
开发语言·c++·算法·matlab