LeetCode Notes_#173_二叉搜索树迭代器
Contents
题目
实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。
调用 next() 将返回二叉搜索树中的下一个最小的数。
示例:
BSTIterator iterator = new BSTIterator(root);
iterator.next(); // 返回 3
iterator.next(); // 返回 7
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 9
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 15
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 20
iterator.hasNext(); // 返回 false
提示:
- next() 和 hasNext() 操作的时间复杂度是 O(1),并使用 O(h) 内存,其中 h 是树的高度。
- 你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 中至少存在一个下一个最小的数。
思路分析
迭代数字的顺序是从小到大,也就是BST的中序遍历的顺序。
方法1:在构造器中得到中序遍历序列
写一个中序遍历的辅助方法,在构造器中调用它,把中序遍历序列保存下来。next()
方法只需要按顺序访问中序遍历序列的每个数字即可。
但是空间复杂度不符合要求。
方法2:拆分非递归中序遍历模板
其实就是非递归中序遍历二叉树的变体,只需要把代码拆分到三个方法当中即可。
以下是非递归中序遍历的模板。
class Solution {
public List<Integer> inOrderTraversal(TreeNode root) {
if (root == null) {
return new LinkedList<>();
}
List<Integer> res = new LinkedList<>();
Deque<TreeNode> stack = new LinkedList<>();
TreeNode node = root;
//以上属于初始化部分,写在构造器中
//以下一行是循环终止条件,写在hasNext()中
while(node != null || !stack.isEmpty()) {
//以下是循环体,写在next()中
while (node != null) {
stack.addLast(node);
node = node.left;
}
node = stack.removeLast();
res.add(node.val);
node = node.right;
}
return res;
}
}
模板大法好。
解答
解答1:在构造器中得到中序遍历序列
不符合空间复杂度要求,仅供参考。
class BSTIterator {
List<Integer> inorder;
int index = 0;
public BSTIterator(TreeNode root) {
inorder = new ArrayList<>();
inorderRecur(root);
}
//得到中序遍历序列
private void inorderRecur(TreeNode root){
if(root == null) return;
inorderRecur(root.left);
inorder.add(root.val);
inorderRecur(root.right);
}
//返回当前节点的值;然后index增加1,指向下一节点
/** @return the next smallest number */
public int next() {
return inorder.get(index++);
}
//判断当前的index索引是否越界
/** @return whether we have a next smallest number */
public boolean hasNext() {
return index <= inorder.size() - 1;
}
}
复杂度分析
构造器
时间复杂度:O(n)
空间复杂度:O(n)
next()
,hasNext()
方法
时间复杂度:O(1)
空间复杂度:O(n)
,因为借助了额外的inorder
数组。
所以是不符合题目要求的,因为题目要求空间复杂度是O(h)
。
解答2:拆分非递归中序遍历模板
//对非递归中序遍历模板进行简单的拆分即可
class BSTIterator {
TreeNode node;
LinkedList<TreeNode> stack;
//初始化部分写到构造器当中
public BSTIterator(TreeNode root) {
node = root;
stack = new LinkedList<>();
}
//next()方法其实就是中序遍历的循环体
/** @return the next smallest number */
public int next() {
while(node != null){
stack.addLast(node);
node = node.left;
}
node = stack.removeLast();
int val = node.val;
node = node.right;
return val;
}
//hasNext()方法就是中序遍历的循环条件
/** @return whether we have a next smallest number */
public boolean hasNext() {
return node != null || !stack.isEmpty();
}
}
复杂度分析
时间复杂度
next()
:所有的n个节点都会入栈,但是并不是每次调用next
就入栈一个,有时候调用一次next
入栈多个节点,有时候则没有节点入栈。整体来看,next()
被调用n次,总共入栈n个节点,平均下来是O(1)
。hasNext()
:这个就不用说了,每次调用的复杂度都是O(1)
空间复杂度:O(h)
- 虽然所有的n个节点都入栈,但是栈中元素最多只有h(即树的深度)。
- 栈中元素最多的时候就是刚开始将最左边的一溜全部入栈的时候,刚好是h。