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

相关推荐
刘马想放假8 小时前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
05Kevin10 小时前
lk每日冒险题--数据结构6.27
算法
To_OC20 小时前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安1 天前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法
七牛开发者1 天前
MCP 到底是什么?为什么 Agent 都想接上它
算法·aigc·agent
北域码匠1 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
kisshyshy1 天前
从递归到迭代,一文吃透二叉树的核心知识与 JavaScript 实现
javascript·算法·代码规范