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

相关推荐
Code_Solitude14 分钟前
c语言:二分法的完整补充
c语言·开发语言·算法
一切皆是因缘际会15 分钟前
掌控信息论:同源星际通信基础理论
人工智能·算法·ai
List<String> error_P2 小时前
freertos数据结构--链表
数据结构·链表
动词ing2 小时前
【C语言题目练习】算法 整数反转
c语言·开发语言·算法
Nil2082 小时前
leetcode 55跳跃游戏
算法·leetcode·游戏
longlongzihan3 小时前
LeetCode 128. 最长连续序列 —— 从排序到哈希集合
数据结构·c++·算法·leetcode·排序算法·哈希表
mmmmath_33 小时前
二叉树の一些知识
数据结构·算法
DongQiShanRen3 小时前
裁决台账双向互校(下):台账哈希链、三向对账与最小落地
人工智能·深度学习·算法·目标跟踪·自然语言处理·rust·哈希算法
程序员清风3 小时前
数据类型转换与日期时间处理
数据结构·pandas
SWAGGY..3 小时前
【C++进阶】:(7)红黑树的原理与 C++ 实现:结构设计、插入调整及性质验证
android·java·开发语言·c++·算法