1. 程式人生 > 實用技巧 >LeetCode Notes_#173_二叉搜尋樹迭代器

LeetCode Notes_#173_二叉搜尋樹迭代器

LeetCode Notes_#173_二叉搜尋樹迭代器

LeetCode

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。