Description

給定一棵二元樹,判斷它是否是有效的二元搜尋樹 (BST)。有效 BST 定義為:左子樹所有節點值 < 根 < 右子樹所有節點值,且左右子樹也都是 BST。

Example:

Input: root = [5,1,4,null,null,3,6] Output: false (因為 4 的左子節點 3 < 根 5,但 4 本身 < 5 不滿足右子樹條件)

Intuition#

每個節點都有合法的值域範圍,遞迴傳遞上下界來驗證。

  • 不能只檢查「左子 < 根 < 右子」,必須檢查整個子樹的值域
  • 中序遍歷 BST 會產生嚴格遞增序列,也可以利用此性質驗證

Approaches#

⭐ 1: 遞迴傳遞上下界#

  • 概念: 每個節點維護 (min, max) 範圍,節點值必須在此範圍內
  • 時間複雜度: O(n)
  • 空間複雜度: O(h)
class Solution {
    fun isValidBST(root: TreeNode?): Boolean {
        return validate(root, Long.MIN_VALUE, Long.MAX_VALUE)
    }

    private fun validate(node: TreeNode?, min: Long, max: Long): Boolean {
        if (node == null) return true
        if (node.`val` <= min || node.`val` >= max) return false
        return validate(node.left, min, node.`val`.toLong()) &&
               validate(node.right, node.`val`.toLong(), max)
    }
}

2: 中序遍歷驗證遞增#

  • 概念: BST 的中序遍歷是嚴格遞增的,遍歷時檢查每個值是否大於前一個
  • 時間複雜度: O(n)
  • 空間複雜度: O(h)
class Solution {
    private var prev: Long = Long.MIN_VALUE

    fun isValidBST(root: TreeNode?): Boolean {
        prev = Long.MIN_VALUE
        return inorder(root)
    }

    private fun inorder(node: TreeNode?): Boolean {
        if (node == null) return true
        if (!inorder(node.left)) return false
        if (node.`val`.toLong() <= prev) return false
        prev = node.`val`.toLong()
        return inorder(node.right)
    }
}
為什麼用 Long 而不是 Int?

因為節點值可能是 Int.MIN_VALUEInt.MAX_VALUE,如果用 Int 作為邊界會導致邊界條件判斷錯誤。使用 Long 可以避免這個問題。

🔑 Takeaways#

  • Pattern: 用範圍約束(上下界)驗證 BST 性質
  • 關鍵技巧: BST 問題常見兩種思路:(1) 遞迴傳遞範圍 (2) 利用中序遍歷的有序性