LeetCode Notes_#173_二叉搜索树迭代器

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。
原文地址:https://www.cnblogs.com/Howfars/p/13463909.html