二叉树——将有序数组转化为平衡二叉搜索树
首先我们先搞懂什么是平衡二叉搜索树
平衡二叉搜索树 = 二叉搜索树+左右高度差不超过1
1.先搞懂:什么是二叉搜索树BST
满足三条:
- 左子树所有节点<根节点
- 右子树所有节点>根节点
- 左右子树也都属二叉搜索树
2.再搞懂平衡
平衡二叉树的定义
对于树中任意一个节点,它的左子树和右子树的高度差不超过1
高度差不能超过1,树就不会歪,不会退化成链表。
3.合起来:平衡二叉搜索树(Banlanced BST)
同时满足两个条件:
1.是二叉搜索树(左小右大)
2.是平衡树(任意节点左右高度差不超过1)
4.为什么有序数组取中间就能变成平衡BST?
因为:
- 中间元素做根->左右两边元素数量几乎相等(当数组长度为偶数时,数量最多差1,奇数数量相等)
- 递归下去每一层都这样
->天然左右高度差不会超过1
->自动平衡
平衡二叉搜索树 = 有序且不歪的二叉树,查找效率 O (log n)
题目要求:
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。
这道题的核心思路是二分法+递归:
因为数组是升序有序的,我们每次取中间元素作为二叉搜索树的根节点,左边子树数组构建左子树,右边子数组构建右子树,这样天然就能保证二叉树是平衡的(左右子树高度差不超过1)。
核心逻辑解释:
1.TreeNode 类:标准的二叉树节点定义,包含值、左孩子、右孩子。
2.主方法 sortedArrayToBST:
- 调用递归方法,传入数组、初始左边界(0)、初始右边界(数组长度 - 1)。
3.递归方法buildTree - 终止条件:left > right,说明当前区间无元素,返回null。
- 取中间值:mid = left + (right - left) / 2,避免(left+right)整数溢出,同时保证取到中间元素。
- 构建根节点:用中间元素创建当前子树的根。
- 递归构建左右子树:
左子树:区间[left, mid-1]
右子树:区间[mid+1, right]
复杂度分析
- 时间复杂度:O (n),每个元素仅访问一次,构建 n 个节点。
- 空间复杂度:O (log n),递归调用栈的深度为平衡二叉树的高度(log n)。
总结
- 有序数组转平衡 BST,核心是二分递归,用中间元素做根保证平衡。
- 递归终止条件是左边界大于右边界,返回null。
- 中间值计算用left + (right-left)/2,避免整数溢出。
3.为什么left>right,返回null?
这段代码是递归的核心逻辑,递归的每一步都是把当前区间 [left, right] 拆成 [left, mid-1](左子树)和 [mid+1, right](右子树)。当拆分后的区间起始位置超过结束位置,就意味着这个方向上没有元素了,自然要返回 null 终止递归。
4.为什么数组长度用nums.length而不是nums.size()?
- nums是数组→ 用 .length(属性)
- 如果是** List(集合)** → 才用 .size()(方法)
这里 nums 是 int [] 数组,不是 List
详细区别:
1. 数组(int []、String []、Object [])
- 是固定大小的 Java 基础结构
- 有一个公开属性叫 length
- 用法:nums.length
2. 集合(ArrayList、LinkedList、HashSet…)
- 是 Java 容器类
- 提供方法 size()
- 用法:list.size()
注意C++中求数组长度也不是用.size()
- C++ 里原始数组:int a[] → 没有 size()!
而是用数组内存大小除以元素内存大小:int len = sizeof(nums) / sizeof(nums[0]); - C++ 里容器:vector
→ 才有 size()
vector 是动态数组容器,属于 STL,才有成员函数 size()。
5.为什么求mid不能用mid =(left+right)/2?
这是一个非常经典、面试必问、且极其重要的细节!
因为 (left + right) 会发生整数溢出(Integer Overflow)
1.溢出是什么?
Java 的 int 最大值是:
2,147,483,647
如果left和right都很大,比如:
- left = 2,000,000,000
- right = 2,000,000,000
left + right = 4,000,000,000
这超过了 int 上限,会变成负数!
然后你再除以 2,得到的 mid 就是错误的负数,直接数组越界崩溃。
2.为什么int mid = left + (right - left) / 2安全?
- right - left 永远不会溢出(因为 right > left,结果一定在 int 范围内)
- 再除以 2,再加 left
- 数学上等价,但绝对安全
一句话总结
(left + right) / 2 会溢出,left + (right - left) / 2 不会溢出,且结果相同。
所以所有二分查找、二分建树,标准写法都是后者。
浙公网安备 33010602011771号