描述

实现一个二叉搜索树迭代器类 BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:

  • BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
  • boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false
  • int next()将指针向右移动,然后返回指针处的数字。

注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。

你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

示例

bst-tree.png

  1. 输入
  2. ["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
  3. [[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
  4. 输出
  5. [null, 3, 7, true, 9, true, 15, true, 20, false]
  6. 解释
  7. BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
  8. bSTIterator.next(); // 返回 3
  9. bSTIterator.next(); // 返回 7
  10. bSTIterator.hasNext(); // 返回 True
  11. bSTIterator.next(); // 返回 9
  12. bSTIterator.hasNext(); // 返回 True
  13. bSTIterator.next(); // 返回 15
  14. bSTIterator.hasNext(); // 返回 True
  15. bSTIterator.next(); // 返回 20
  16. bSTIterator.hasNext(); // 返回 False

提示

  • 树中节点的数目在范围 [1, 105]
  • 0 <= Node.val <= 106
  • 最多调用 105hasNextnext 操作

    解题思路

    解法一

    寻找二叉搜索树的中序后继的方法

    代码

    class BSTIterator {
      TreeNode ptr;
      TreeNode root;
      public BSTIterator(TreeNode root) {
          ptr = new TreeNode(-1);
          this.root = root;
      }
    
      public int next() {
          int res = -1;
          TreeNode dummy = root;
          while (dummy != null) {
              if (dummy.val > ptr.val) {
                  res = dummy.val;
                  dummy = dummy.left;
              } else {
                  dummy = dummy.right;
              }
          }
          ptr = new TreeNode(res);
          return res;    
      }
    
      public boolean hasNext() {
          TreeNode dummyPtr = ptr;
          boolean hasNext = next() == -1 ? false : true;
          ptr = dummyPtr;
          return hasNext;
      }
    }
    

    解法二:扁平化

我们可以直接对二叉搜索树做一次完全的递归遍历,获取中序遍历的全部结果并保存在数组中。随后,我们利用得到的数组本身来实现迭代器。

代码


class BSTIterator {
    private int idx;
    private List<Integer> arr;

    public BSTIterator(TreeNode root) {
        idx = 0;
        arr = new ArrayList<Integer>();
        inorderTraversal(root, arr);
    }

    public int next() {
        return arr.get(idx++);
    }

    public boolean hasNext() {
        return idx < arr.size();
    }

    private void inorderTraversal(TreeNode root, List<Integer> arr) {
        if (root == null) {
            return;
        }
        inorderTraversal(root.left, arr);
        arr.add(root.val);
        inorderTraversal(root.right, arr);
    }
}

复杂度分析

时间复杂度:初始化需要 O(n) 的时间,其中 n 为树中节点的数量。随后每次调用只需要 O(1) 的时间。
空间复杂度:O(n),因为需要保存中序遍历的全部结果。

方法三:迭代

除了递归的方法外,我们还可以利用栈这一数据结构,通过迭代的方式对二叉树做中序遍历。此时,我们无需预先计算出中序遍历的全部结果,只需要实时维护当前栈的情况即可。

代码

class BSTIterator {
    private TreeNode cur;
    private Deque<TreeNode> stack;

    public BSTIterator(TreeNode root) {
        cur = root;
        stack = new LinkedList<TreeNode>();
    }

    public int next() {
        while (cur != null) {
            stack.push(cur);
            cur = cur.left;
        }
        cur = stack.pop();
        int ret = cur.val;
        cur = cur.right;
        return ret;
    }

    public boolean hasNext() {
        return cur != null || !stack.isEmpty();
    }
}

复杂度分析

时间复杂度:显然,初始化和调用 hasNext() 都只需要 O(1) 的时间。每次调用 next() 函数最坏情况下需要 O(n) 的时间;但考虑到 n 次调用 next() 函数总共会遍历全部的 n 个节点,因此总的时间复杂度为 O(n),因此单次调用平均下来的均摊复杂度为 O(1)。

空间复杂度:O(n),其中 n 是二叉树的节点数量。空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别。