LeetCode LCR 055. 二叉搜索树迭代器
题目描述



题意分析
实现按中序顺序读取二叉搜索树的迭代器:
next返回下一个节点值,hasNext判断是否仍有未返回的节点。第一次读取应得到最小值,题目保证next只在有后继时调用。进阶要求操作均摊常数时间、额外空间为树高
h的量级。可以把中序遍历的进度保存在栈中,每次只推进到下一个需要返回的节点。
解法:受控中序遍历
核心思路
[!blue]
中序遍历先访问左子树,再访问当前节点,最后访问右子树。用一个保存在迭代器对象中的栈,记录已经找到、但尚未返回的节点;方法返回后栈仍保留,下次调用就能接着遍历。
构造时从根沿左孩子一路压栈,直到空节点。最左端节点位于栈顶,也就是第一个答案;下面的节点是以后还需要返回的祖先。
每次调用
next,先弹出栈顶cur。它左侧应返回的节点都已经处理完,因此现在轮到它。接下来中序会进入cur的右子树,所以再把右子树的整条左链压栈,让该子树最小的节点成为新的栈顶。若没有右子树,不压入任何节点,栈顶就自然回到下一个尚未返回的祖先。每次操作结束时,栈顶始终是下一个最小的未返回节点,栈内其余节点只记录后续还需要访问的祖先。
每个未处理子树都会在它前面的节点返回后接入这套过程,因此栈为空就代表没有剩余节点。
hasNext只判断栈是否为空,不能弹栈或提前返回节点。
解题步骤
- 构造时调用压左链操作,从根开始把所有沿途节点入栈。
hasNext直接返回栈是否非空,不改变遍历进度。next弹出栈顶节点,并从它的右孩子出发,把整条左链加入栈。- 返回弹出节点的值。不断重复后,节点依次按中序被返回。
代码实现
class BSTIterator {
private Deque<TreeNode> stack = new ArrayDeque<>();
public BSTIterator(TreeNode root) {
pushLeft(root);
}
public int next() {
TreeNode cur = stack.pop();
pushLeft(cur.right);
return cur.val;
}
public boolean hasNext() {
return !stack.isEmpty();
}
private void pushLeft(TreeNode node) {
for (; node != null; node = node.left) {
stack.push(node);
}
}
}
type BSTIterator struct {
stack []*TreeNode
}
func Constructor(root *TreeNode) BSTIterator {
var stack []*TreeNode
for ; root != nil; root = root.Left {
stack = append(stack, root)
}
return BSTIterator{
stack: stack,
}
}
func (this *BSTIterator) Next() int {
cur := this.stack[len(this.stack)-1]
this.stack = this.stack[:len(this.stack)-1]
for node := cur.Right; node != nil; node = node.Left {
this.stack = append(this.stack, node)
}
return cur.Val
}
func (this *BSTIterator) HasNext() bool {
return len(this.stack) > 0
}
复杂度分析
设节点数为
n,树高为h。
- 时间复杂度:构造为 $O(h)$,
hasNext为 $O(1)$。单次next最坏为 $O(h)$,因为可能压入一条左链;完整遍历中每个节点只入栈、出栈各一次,总计 $O(n)$,所以next均摊为 $O(1)$。- 空间复杂度:$O(h)$,栈中保存的是当前路径上尚未返回的部分节点,而不是全部节点值。
关键点总结
[!green]
- 栈保存可以跨调用保留的中序遍历进度,栈顶始终是下一个答案。
- 返回当前节点后,应转向右子树,并沿左链找到它的最小节点。
- 单次调用可能较慢,但每个节点的入栈和出栈总次数固定,满足均摊常数时间。
易错点总结
[!yellow]
- 构造时只压根节点:根未必是最小值,必须先压入整条左链。
- 弹出后只压右孩子:右孩子可能还有更小的左后代,仍需压入它的整条左链。
- 再次进入当前节点的左子树:会重复返回已经处理的节点。
- 在
hasNext中弹栈:查询是否还有元素也会消耗遍历进度。- 把均摊常数时间写成每次都为常数:某一次
next可能遍历深度为h的左链。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 94. 二叉树的中序遍历 | 简单 | 同样用显式栈模拟中序,本题把一次完整遍历拆成多次next调用,保留尚未访问的路径。 |
| 285. 二叉搜索树中的中序后继 | 中等 | 本题持续产生后继序列,原题给定一个节点后直接查询一次中序后继。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!