LC108 · 将有序数组转换为二叉搜索树 · 算法执行实验台
有序数组 · 中点分治 · 平衡 BST
本题不是简单地把元素塞进 BST,而是同时满足: BST(左 < 根 < 右)与 高度平衡(左右子树高度差尽量小)。 核心手段:在有序数组上每次选 mid 作根,把区间均分给左右递归。
核心机制 · 选中点分治
- · 本题同时满足两件事:BST(左 < 根 < 右)+ 高度平衡(左右子树高度差尽量小)。
- · 数组已有序:选 mid 作根时,左半都小于根、右半都大于根,天然满足 BST。
- · 总选左端会让树退化成链表;选中点能把数组平均分给左右,树尽量矮。
- · build(lo, hi) 的语义:把 nums[lo..hi] 这一段有序数组转换成一棵高度平衡 BST。
数组已有序,选 mid 天然满足 BST;均分左右保证平衡。递归边界 lo > hi → nil。
- mid := lo + (hi-lo)/2
- node.Left = build(lo, mid-1)
- node.Right = build(mid+1, hi)
- build(0,4) → mid=2, root=0
- ├ build(0,1) → mid=0, root=-10
- │ └ build(1,1) → root=-3
- └ build(3,4) → mid=3, root=5
- └ build(4,4) → root=9
读题:不只是建 BST,还要高度平衡
给定升序数组 nums,转成一棵二叉搜索树,且要「高度平衡」——任意节点左右子树高度差不超过 1。两个约束同时成立:BST 性质(左 < 根 < 右)+ 分治时让左右规模尽量接近。示例 nums = [-10, -3, 0, 5, 9]。
· 本题同时满足两件事:BST(左 < 根 < 右)+ 高度平衡(左右子树高度差尽量小)。
· 数组已有序:选 mid 作根时,左半都小于根、右半都大于根,天然满足 BST。
· 总选左端会让树退化成链表;选中点能把数组平均分给左右,树尽量矮。
· build(lo, hi) 的语义:把 nums[lo..hi] 这一段有序数组转换成一棵高度平衡 BST。
本题同时满足两件事:BST(左 < 根 < 右)+ 高度平衡(左右子树高度差尽量小)。
递归栈 · build(lo, hi)
(空栈)
状态表 · lo / hi / mid / 栈
| lo | hi | mid | root | 左区间 | 右区间 | 调用栈 |
|---|---|---|---|---|---|---|
| 0 | 4 | — | — | ∅ | ∅ | [] |
当前切片:[-10, -3, 0, 5, 9]
读题BST + 平衡
1func sortedArrayToBST(nums []int) *TreeNode {2 var build func(lo, hi int) *TreeNode3 build = func(lo, hi int) *TreeNode {4 if lo > hi {5 return nil6 }7 mid := lo + (hi-lo)/28 node := &TreeNode{Val: nums[mid]}9 node.Left = build(lo, mid-1)10 node.Right = build(mid+1, hi)11 return node12 }13 return build(0, len(nums)-1)14}
- 时间
- O(n)
- 辅助空间
- O(log n)
- 总空间(含输出树)
- O(n)
每个数组元素只创建一次节点,共 n 次递归建树。
递归调用栈深度等于平衡 BST 高度,约 log n。
若把输出的 n 个节点计入,总空间为 O(n);辅助空间仍是 O(log n) 栈。
数组已经有序,所以选择中间元素作为根时,左半部分都小于根,右半部分都大于根,天然满足 BST。为了让树高度平衡,每次都选择中点,把数组平均分给左右子树。递归处理左右区间即可。每个元素只创建一次节点,时间 O(n)。递归深度是平衡树高度,所以辅助空间 O(log n)。
mid 取左中位数 (lo+hi)/2 或右中位数 (lo+hi+1)/2 都可能得到合法的高度平衡 BST,LeetCode 接受多种结构。
忽略了递归栈 O(log n)。输出树本身占 O(n),但面试里「辅助空间」通常指栈,不是 O(1)。
必须解释「选中点 → 左右规模接近 → 树高 O(log n)」,否则面试官会追问 mid 的意义。
lo > hi 必须 return nil;mid-1 / mid+1 切分不能漏,否则越界或无限递归。