LeetCode 第108题:将有序数组转换为二叉搜索树
题目描述
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
难度
简单
题目链接
示例
示例 1:
输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
示例 2:
输入:nums = [1,3]
输出:[3,1]
解释:[1,3] 和 [3,1] 都是高度平衡二叉搜索树。
提示
1 <= nums.length <= 10^4-10^4 <= nums[i] <= 10^4nums按 严格递增 顺序排列
解题思路
方法:二分递归
这道题要求将一个有序数组转换为高度平衡的二叉搜索树。由于数组已经排序,我们可以利用二分法来构建平衡二叉搜索树。
关键点:
- 选择数组中间元素作为根节点,这样可以保证左右子树的节点数量尽可能接近
- 对左半部分数组递归构建左子树
- 对右半部分数组递归构建右子树
- 将左右子树连接到根节点
具体步骤:
- 如果数组为空,返回null
- 找到数组的中间位置mid
- 创建一个新节点,值为nums[mid]
- 递归构建左子树:使用nums[0...mid-1]
- 递归构建右子树:使用nums[mid+1...n-1]
- 返回根节点
时间复杂度:O(n),其中n是数组的长度,每个元素只访问一次 空间复杂度:O(log n),递归调用栈的深度
图解思路
递归构建过程分析表
| 步骤 | 当前数组范围 | 中间位置 | 根节点值 | 左子树范围 | 右子树范围 |
|---|---|---|---|---|---|
| 1 | [-10,-3,0,5,9] | 2 | 0 | [-10,-3] | [5,9] |
| 2 | [-10,-3] | 0 | -10 | [] | [-3] |
| 3 | [-3] | 0 | -3 | [] | [] |
| 4 | [5,9] | 0 | 5 | [] | [9] |
| 5 | [9] | 0 | 9 | [] | [] |
构建树的过程示意图
| 阶段 | 构建的树 | 说明 |
|---|---|---|
| 初始 | null | 开始构建 |
| 步骤1 | 0 | 选择中间元素0作为根节点 |
| 步骤2 | 0<br>/ \ | 递归处理左右子树 |
| 步骤3 | 0<br> / \ <br>-10 5 | 构建第二层节点 |
| 步骤4 | 0<br> / \ <br>-10 5<br> \ \ <br> -3 9 | 构建第三层节点 |
| 最终 | 0<br> / \ <br>-10 5<br> \ \ <br> -3 9 | 完成构建 |
代码实现
C# 实现
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
public TreeNode SortedArrayToBST(int[] nums) {
if (nums == null || nums.Length == 0) {
return null;
}
return BuildBST(nums, 0, nums.Length - 1);
}
private TreeNode BuildBST(int[] nums, int left, int right) {
// 基本情况:如果左边界大于右边界,返回null
if (left > right) {
return null;
}
// 选择中间位置作为根节点
int mid = left + (right - left) / 2;
TreeNode root = new TreeNode(nums[mid]);
// 递归构建左子树和右子树
root.left = BuildBST(nums, left, mid - 1);
root.right = BuildBST(nums, mid + 1, right);
return root;
}
}
Python 实现
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return None
def build_bst(left, right):
# 基本情况:如果左边界大于右边界,返回None
if left > right:
return None
# 选择中间位置作为根节点
mid = left + (right - left) // 2
root = TreeNode(nums[mid])
# 递归构建左子树和右子树
root.left = build_bst(left, mid - 1)
root.right = build_bst(mid + 1, right)
return root
return build_bst(0, len(nums) - 1)
C++ 实现
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
if (nums.empty()) {
return nullptr;
}
return buildBST(nums, 0, nums.size() - 1);
}
private:
TreeNode* buildBST(vector<int>& nums, int left, int right) {
// 基本情况:如果左边界大于右边界,返回nullptr
if (left > right) {
return nullptr;
}
// 选择中间位置作为根节点
int mid = left + (right - left) / 2;
TreeNode* root = new TreeNode(nums[mid]);
// 递归构建左子树和右子树
root->left = buildBST(nums, left, mid - 1);
root->right = buildBST(nums, mid + 1, right);
return root;
}
};
执行结果
C# 实现
- 执行用时:84 ms
- 内存消耗:41.1 MB
Python 实现
- 执行用时:48 ms
- 内存消耗:17.9 MB
C++ 实现
- 执行用时:12 ms
- 内存消耗:21.3 MB
性能对比
| 语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C# | 84 ms | 41.1 MB | 代码结构清晰,易于理解 |
| Python | 48 ms | 17.9 MB | 使用闭包函数实现递归,代码简洁 |
| C++ | 12 ms | 21.3 MB | 执行效率最高,内存占用适中 |
代码亮点
- 🎯 使用二分法选择中间元素作为根节点,保证树的平衡性
- 💡 递归构建左右子树,代码简洁高效
- 🔍 精确计算中间位置,避免整数溢出
- 🎨 代码结构清晰,变量命名规范,易于理解
常见错误分析
- 🚫 计算中间位置时使用
(left + right) / 2,可能导致整数溢出 - 🚫 没有正确处理边界情况,如空数组或只有一个元素的数组
- 🚫 递归终止条件设置不当,可能导致无限递归
- 🚫 左右子树范围计算错误,导致构建的树不平衡
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 二分递归 | O(n) | O(log n) | 简单直观,保证平衡性 | 递归调用可能导致栈溢出 |
| 迭代实现 | O(n) | O(log n) | 避免递归栈溢出 | 实现复杂,不直观 |
| 中序遍历构建 | O(n) | O(log n) | 可以生成多种平衡树 | 需要额外的索引管理 |
相关题目
- LeetCode 109. 有序链表转换二叉搜索树 - 中等
- LeetCode 110. 平衡二叉树 - 简单
- LeetCode 1382. 将二叉搜索树变平衡 - 中等
- LeetCode 96. 不同的二叉搜索树 - 中等