BST 經典問題#

驗證二元搜尋樹 #

中序走訪#

核心原理:BST 的中序走訪結果必為嚴格遞增序列

fun isValidBST(root: TreeNode?): Boolean {
    var prev = Long.MIN_VALUE   // 前一個中序節點值,初始為 -∞

    fun inorder(node: TreeNode?): Boolean {
        if (node == null) return true

        if (!inorder(node.left)) return false

        if (node.value <= prev) return false   // 不是嚴格遞增
        prev = node.value.toLong()

        return inorder(node.right)
    }

    return inorder(root)
}

空間最佳化:不需存整個走訪序列,只需維護「前一個節點值」即可。

遞迴區間驗證#

傳遞允許的數值範圍 (min, max)

fun isValidBST(root: TreeNode?): Boolean {
    // 用 Long 承載邊界,避免節點值等於 Int.MIN/MAX_VALUE 時比較失準
    fun validate(node: TreeNode?, min: Long, max: Long): Boolean {
        if (node == null) return true

        if (node.value <= min || node.value >= max) return false

        return validate(node.left, min, node.value.toLong()) &&
                validate(node.right, node.value.toLong(), max)
    }

    return validate(root, Long.MIN_VALUE, Long.MAX_VALUE)
}

最近公共祖先#

普通二元樹 #

使用後序走訪,自底向上回傳:

fun lowestCommonAncestor(root: TreeNode?, p: TreeNode, q: TreeNode): TreeNode? {
    if (root == null || root == p || root == q) return root

    val left = lowestCommonAncestor(root.left, p, q)
    val right = lowestCommonAncestor(root.right, p, q)

    if (left != null && right != null) return root   // p, q 分居兩側

    return left ?: right
}

精髓:回傳值具有多重含義 - 既表示「找到了 p 或 q」,也表示「找到了 LCA」。

BST 版本 #

利用 BST 的排序特性,迭代實作:

fun lowestCommonAncestor(root: TreeNode?, p: TreeNode, q: TreeNode): TreeNode? {
    var node = root
    while (node != null) {
        node = when {
            p.value < node.value && q.value < node.value -> node.left   // 都在左邊
            p.value > node.value && q.value > node.value -> node.right  // 都在右邊
            else -> return node                                          // 分岔點即為 LCA
        }
    }
    return null
}

優勢:BST 版本可用迭代,空間複雜度 O(1)


二元樹的深度:最大深度 / 最小深度 #

最大深度#

fun maxDepth(root: TreeNode?): Int {
    if (root == null) return 0
    return maxOf(maxDepth(root.left), maxDepth(root.right)) + 1
}

最小深度#

陷阱:不能直接 minOf(left, right) + 1!單邊子樹為空時不算葉節點。

fun minDepth(root: TreeNode?): Int {
    if (root == null) return 0

    val left = minDepth(root.left)
    val right = minDepth(root.right)

    // 處理單邊子樹為空的情況
    if (left == 0 || right == 0) return left + right + 1

    return minOf(left, right) + 1
}