LeetCode 108:将有序数组转换为二叉搜索树 —— 从数组到平衡二叉树的递归构造

一、题目描述

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

高度平衡二叉树:每个节点的左右两个子树的高度差不超过 1。

示例:

输入:

复制代码
nums = [-10,-3,0,5,9]

输出:

复制代码
      0
     / \
   -3   9
   /   /
 -10  5

对应数组:

复制代码
[-10,-3,0,5,9]

构造成:

复制代码
        0
       / \
     -3   9
    /    /
 -10    5

二、为什么这道题值得学习?

这道题是二叉树构造类问题的经典入门题,也是面试中经常考察的递归应用。

它考察的核心:

1. 二叉搜索树性质

二叉搜索树(BST)满足:

  • 左子树所有节点 < 根节点
  • 右子树所有节点 > 根节点

因为数组本身已经有序,所以我们可以直接利用中间位置作为根节点。


2. 平衡思想

如果每次都选择数组中间元素作为根节点:

  • 左半部分构造左子树
  • 右半部分构造右子树

那么左右子树规模接近,可以保证树的高度平衡。


3. 递归构造思维

这道题本质是:

用一个数组区间,构造一棵对应的二叉树。

核心就是:

复制代码
确定根节点
↓
递归构造左子树
↓
递归构造右子树

三、核心思想:选择中间节点作为根

因为数组是升序:

例如:

复制代码
[-10,-3,0,5,9]

中间元素:

复制代码
0

作为根节点。

那么:

左边:

复制代码
[-10,-3]

继续构造左子树。

右边:

复制代码
[5,9]

继续构造右子树。

过程:

复制代码
              0
            /   \
       [-10,-3] [5,9]


              0
            /   \
          -3     9
         /       /
       -10      5

不断重复这个过程,直到数组为空。


四、递归三部曲

做递归题,可以按照三个步骤分析:

1. 确定递归函数

我们定义:

复制代码
buildTree(nums, left, right)

含义:

使用 numsleft...right 这个区间构造一棵二叉搜索树,并返回根节点。


2. 确定递归终止条件

当:

复制代码
left > right

说明数组区间为空。

没有节点:

复制代码
return null;

3. 确定单层递归逻辑

步骤:

第一步:找到中间位置

复制代码
int mid = left + (right - left) / 2;

避免:

复制代码
(left + right)

可能产生整数溢出。


第二步:创建根节点

复制代码
TreeNode root = new TreeNode(nums[mid]);

第三步:递归构造左右子树

左区间:

复制代码
[left, mid-1]

右区间:

复制代码
[mid+1, right]

代码:

复制代码
root.left = buildTree(nums,left,mid-1);

root.right = buildTree(nums,mid+1,right);

五、解法:递归法(面试首选 ✅)

Java代码

复制代码
class Solution {

    public TreeNode sortedArrayToBST(int[] nums) {

        return buildTree(nums,0,nums.length-1);

    }


    private TreeNode buildTree(int[] nums,int left,int right){

        // 区间为空
        if(left > right){
            return null;
        }


        // 找中间节点
        int mid = left + (right-left)/2;


        // 创建根节点
        TreeNode root = new TreeNode(nums[mid]);


        // 构造左子树
        root.left = buildTree(nums,left,mid-1);


        // 构造右子树
        root.right = buildTree(nums,mid+1,right);


        return root;
    }
}

六、过程图解

数组:

复制代码
[-10,-3,0,5,9]

第一次:

复制代码
mid=2

       0

左区间:
[-10,-3]

右区间:
[5,9]

第二次:

左:

复制代码
[-10,-3]

mid=0

      -10
          \
          -3

右:

复制代码
[5,9]

mid=3

       5
          \
           9

最终:

复制代码
          0
        /   \
      -10    5
        \      \
        -3      9

七、复杂度分析

时间复杂度:O(N)

原因:

数组中的每个元素都会被创建成一个节点。

所以:

复制代码
访问 N 个元素

时间复杂度:

复制代码
O(N)

空间复杂度:O(logN)

递归调用栈的深度取决于树的高度。

因为构造的是平衡二叉树:

高度约为:

复制代码
logN

所以:

复制代码
O(logN)

八、常见错误与避坑指南

❌ 错误一:直接选择第一个元素作为根节点

例如:

复制代码
[-10,-3,0,5,9]

如果:

复制代码
root=-10

会形成:

复制代码
-10
   \
   -3
      \
       0
        \
         5
          \
           9

退化成链表。

失去平衡。


❌ 错误二:mid计算方式错误

错误:

复制代码
int mid=(left+right)/2;

虽然一般情况下没问题,但存在整数溢出的风险。

推荐:

复制代码
int mid=left+(right-left)/2;

❌ 错误三:递归区间写错

左子树:

正确:

复制代码
left -> mid-1

错误:

复制代码
left -> mid

会导致重复使用中间节点。

右子树:

正确:

复制代码
mid+1 -> right

九、面试高频追问

1️⃣ 为什么一定选择中间节点?

因为数组已经有序。

选择中间节点:

  • 左右元素数量接近
  • 左右子树高度接近
  • 可以保证平衡

2️⃣ 可以不用递归吗?

可以。

可以使用队列模拟递归:

保存:

复制代码
数组区间 + 对应树节点

但是代码复杂度更高。

面试中递归更加自然。


3️⃣ 如果数组不是升序怎么办?

需要先排序:

复制代码
排序 O(NlogN)
+
构造 O(N)

整体:

复制代码
O(NlogN)
相关推荐
战族狼魂6 小时前
高频面试题精选:分治与AI Agent架构
人工智能·算法·大模型·大语言模型
李剑一6 小时前
你用过网易的将军令嘛?它底层实现账号保护的原理相当简单粗暴
算法
Scott9999HH6 小时前
告别流量波动玄学!从法拉第电磁定律到底层 C/C++ 流量累积算法,深度解密工业级电磁流量计选型与开发
c语言·c++·算法
中达瑞和-高光谱·多光谱7 小时前
1nm到8nm光谱分辨率,你的应用该选哪款光谱相机?
算法·高光谱·高光谱相机
ChaoZiLL7 小时前
我的数据结构4-栈和队列
数据结构
香辣牛肉饭8 小时前
【算法】动态规划 最长公共子序列(LCS)
经验分享·笔记·算法·动态规划
miller-tsunami8 小时前
顺序表相关知识点
数据结构·顺序表
旖-旎8 小时前
LeetCode 279:完全平方数(完全背包)—— 题解
c++·算法·leetcode·动态规划·背包问题
.徐十三.9 小时前
一篇文章看到最短路径——Dijkstra算法
算法