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

相关推荐
2401_872418786 小时前
算法入门:数据结构-堆
数据结构·算法
xwz小王子7 小时前
手术机器人登上Science Robotics:2毫米纤细手臂,从3厘米切口完成腰椎神经减压
算法·机器人
黎阳之光8 小时前
视频孪生智护供水生命线:黎阳之光赋能医疗与园区水务高质量升级
运维·物联网·算法·安全·数字孪生
Black蜡笔小新9 小时前
自动化AI算法训练服务器DLTM制造业AI质检工作站助力制造业实现AI智检
人工智能·算法·自动化
嵌入式小能手9 小时前
飞凌嵌入式ElfBoard-进程间的通信之命名管道
linux·服务器·算法
啦哈拉哈9 小时前
Leetcode题解记录-hot100(81-100)
算法·leetcode·职场和发展
csdn_aspnet9 小时前
Java 霍尔分区算法(Hoare‘s Partition Algorithm)
java·开发语言·算法
诸葛务农9 小时前
道路行驶条件下电动汽车永磁电机的有效使用寿命及永磁体的失效和回收再利用(下)
java·开发语言·算法
snow@li9 小时前
AI:理解 大数据、算法、算力、电力、生成式AI、token 之间的关系
大数据·人工智能·算法
小智老师PMP10 小时前
零基础能不能考PMP?零基础专属学习路径+全套扶持体系
学习·算法·职场和发展·软件工程·求职招聘·敏捷流程