LeetCode 樹 98. 驗證二叉搜索樹(二叉搜索樹,遞歸,中序遍歷,迭代)

中序。

 

 剛拿到題目時,第一想法是遞歸,但是搞錯了二叉搜索樹成立的條件

我以爲的條件是:左側樹爲二叉搜索樹,右側樹爲二叉搜索樹,且root.right>root>root.left,然後遞歸

但是顯然這不對,滿足以上條件後,root.right.left可能比root要小

 

先說正確的遞歸解法:
正確的條件是:

  • 左子樹和右子樹都是二叉搜索樹
  • root的值需要大於root左子樹的所有值
  • root的值需要小於root右子樹的所有值

後兩個條件可以轉化爲更好寫代碼的兩個條件:

  • 當前節點的值是其左子樹的值的上界(最大值)
  • 當前節點的值是其右子樹的值的下界(最小值)

抽象爲這個圖

 

 

遞歸函數: helper(root,lower,upper)

 

遞歸函數的作用:以root爲根節點的樹,滿足lower和upper的上下界限制

終止條件:1.當前節點時葉子節點,返回true 2.當前節點本身已經不滿足[lower,upper]的上下界限制,返回false

遞歸關係:以root爲根節點的樹滿足lower和upper的上下界要求的條件是:首先root得滿足[lower,upper]的上下界限制,其次root.left需滿足[lower,root.val]的上下界要求,且root.right滿足[root.val,upper]的上下界要求

這個遞歸關係用語言描述起來感覺很模糊,但是對照着上一張圖看起來就很清晰了。

class Solution {
  public boolean helper(TreeNode node, Integer lower, Integer upper) {
    if (node == null) return true;

    int val = node.val;
    if (lower!=null && val <= lower) return false;
    if (upper!=null && val >= upper) return false;

    if (! helper(node.right, val, upper)) return false;
    if (! helper(node.left, lower, val)) return false;
    return true;
  }

  public boolean isValidBST(TreeNode root) {
    return helper(root, null, null);
  }
}

代碼有幾處需要注意:

  • 二叉搜索樹是嚴格的,即root.left=root不可以,所以val = lower也要判定爲false
  • 初始條件時的上下界寫什麼。初始條件的意思是,第一個上下界不受限制,開始本來是準備寫Integer.Max,但是發現有個測試用例真就是Integer.Max。所以最後採取這個null的寫法,看到界爲null,默認這個界不做判斷。
  • 條件中的與要用&&否則爲null時一判斷第二個條件就會報錯。

所以這個解法真的好多坑啊。。。

 

下一種最容易看懂的方式,是以遞歸實現中序遍歷,把二叉樹的值中序遍歷存到一個List裏,然後檢查這個List是否是升序的就行了

class Solution {
    List<Integer> res = new ArrayList<>();
    public boolean isValidBST(TreeNode root) {
        if(root==null)
            return true;
        inOrder(root);
        for(int i=1;i<res.size();i++){
            if(res.get(i)<=res.get(i-1)){
                return false;
            }
        }
        return true;
    }

    private void inOrder(TreeNode root){
        if(root!=null){
            inOrder(root.left);
            res.add(root.val);
            inOrder(root.right);
        }
    }
}

這種解法無比容易看懂,且時間複雜度是On,空間複雜度是On。但是當然還是要慢一些,如果直接通過迭代的方式實現中序遍歷(壓棧),就可以在遍歷的過程中進行檢測了。或者也可以通過遞歸的方式實現中序遍歷,並在其中做判斷

class Solution {
    long pre = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true;
        }
        // 訪問左子樹
        if (!isValidBST(root.left)) {
            return false;
        }
        // 訪問當前節點:如果當前節點小於等於中序遍歷的前一個節點,說明不滿足BST,返回 false;否則繼續遍歷。
        if (root.val <= pre) {
            return false;
        }
        pre = root.val;
        // 訪問右子樹
        return isValidBST(root.right);
    }
}

這樣很巧妙,但是更難理解一些。。。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章