LC109 · 有序链表转换二叉搜索树
链表版 LC108:不能随机访问 mid,怎么办?
LC108 在数组上 mid = (lo+hi)/2 是 O(1);链表只能顺序走。 主线用快慢指针找中点、prev.Next = nil 断链后递归——时间 O(n log n)。进阶可用中序模拟做到 O(n)。
主线 · 链表版 LC108
- · LC109 是 LC108 的链表版本:同样要把升序序列变成高度平衡 BST。
- · LC108 的数组可以 O(1) 取 mid = (lo+hi)/2;链表不能随机访问,难点是如何找/模拟中点。
- · 主线:快慢指针找中点 → prev.next = nil 断链 → 递归左右半段。
- · 每层递归都要扫描当前链表找中点,总时间 O(n log n),不是 O(n)。
- 1. slow / fast 找当前链表 mid
- 2. prev.Next = nil 切断左段
- 3. root = mid,左 = build(head),右 = build(slow.Next)
转数组套 LC108 是 O(n) 补充思路,不在本动画主线里展开。
- 每一层递归都要扫描当前链表找中点(快慢指针走一遍)。
- 1.第 1 层:扫描 n 个节点。
- 2.第 2 层:左右两段各约 n/2,合计仍约 n 次指针移动。
- 3.共有约 log n 层,每层总扫描量同阶于 n。
- 4.所以总时间 = n × log n = O(n log n),不能写成 O(n)。
- 1.先遍历链表得到长度 n。
- 2.递归 build(l, r) 时,先构造左子树。
- 3.当前 head 就是中序序列里的 root。
- 4.创建 root 后执行 head = head.next。
- 5.再构造右子树。
- 6.本质:树的结构由区间大小决定,节点值按链表顺序中序填入。
链表版 LC108:升序链表 → 高度平衡 BST
输入是升序链表 -10 → -3 → 0 → 5 → 9。目标与 LC108 相同:生成一棵高度平衡的二叉搜索树。LC108 在数组上 mid = (lo+hi)/2 是 O(1);链表没有下标,不能随机访问中点——这是 LC109 的额外难点。
· LC109 是 LC108 的链表版本:同样要把升序序列变成高度平衡 BST。
· LC108 的数组可以 O(1) 取 mid = (lo+hi)/2;链表不能随机访问,难点是如何找/模拟中点。
· 主线:快慢指针找中点 → prev.next = nil 断链 → 递归左右半段。
· 每层递归都要扫描当前链表找中点,总时间 O(n log n),不是 O(n)。
LC108 的数组可以 O(1) 取 mid = (lo+hi)/2;链表不能随机访问,难点是如何找/模拟中点。
状态表 · 链表 / 指针 / 递归
| 视图 | slow | fast | mid | 断点 | 调用栈 |
|---|---|---|---|---|---|
| 全链表 | — | — | — | — | [] |
当前段:-10 → -3 → 0 → 5 → 9 → null
读题对比 LC108:数组 O(1) 取 mid vs 链表需扫描
递归栈 · sortedListToBST
(空栈 · 建树完成)
1func sortedListToBST(head *ListNode) *TreeNode {2 if head == nil { return nil }3 if head.Next == nil {4 return &TreeNode{Val: head.Val}5 }6 slow, fast := head, head7 var prev *ListNode8 for fast != nil && fast.Next != nil {9 prev = slow10 slow = slow.Next11 fast = fast.Next.Next12 }13 if prev != nil { prev.Next = nil }14 root := &TreeNode{Val: slow.Val}15 root.Left = sortedListToBST(head)16 root.Right = sortedListToBST(slow.Next)17 return root18}
每一层递归都要用快慢指针扫描当前链表找中点;共约 log n 层,每层总扫描约 n 个节点。 递归栈深度等于平衡 BST 高度,约 O(log n)。
遍历链表入数组 O(n),再按 LC108 分治建树 O(n)。 需要 O(n) 额外数组存储节点值。
先统计长度 O(n),再按区间递归,每个节点只访问一次。 递归栈 O(log n);用全局 head 指针按中序顺序填值。
左半段没有切断时,左递归仍指向完整链表,会死循环或栈溢出。
只有 head 一个节点时应直接 return &TreeNode{Val: head.Val},否则 prev 为空、断链逻辑会出错。
每层扫描一次链表,log n 层叠加才是 O(n log n),不是「每个节点只访问一次」。
只要高度平衡且中序有序即可;右子树 [5,9] 取 5 或 9 作根都可能合法。
本题只要求分治后树高 O(log n),不需要旋转维护严格 AVL。