Description

給定一棵 BST 的根節點,將每個節點的值替換為原 BST 中所有大於或等於該節點值的節點值之和。

Example:

Input: root = [4,1,6,0,2,5,7,null,null,null,3,null,null,null,8] Output: [30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

Intuition#

反向中序遍歷(右 -> 根 -> 左)就是從大到小遍歷,累加即可。

  • BST 中序遍歷(左 -> 根 -> 右)得到遞增序列
  • 反向中序遍歷(右 -> 根 -> 左)得到遞減序列
  • 維護一個累加和,每到一個節點就加上累加和,更新節點值

Approaches#

1: 反向中序遍歷(遞迴)#

  • 概念: 反向中序遍歷,用全域變數累加,每個節點更新為累加和
  • 時間複雜度: O(n)
  • 空間複雜度: O(h),h 為樹高
class Solution {
    private var sum = 0

    fun convertBST(root: TreeNode?): TreeNode? {
        sum = 0
        reverseInorder(root)
        return root
    }

    private fun reverseInorder(node: TreeNode?) {
        if (node == null) return
        reverseInorder(node.right)
        sum += node.`val`
        node.`val` = sum
        reverseInorder(node.left)
    }
}

⭐ 2: 反向中序遍歷(迭代)#

  • 概念: 用堆疊模擬反向中序遍歷,先一路向右,然後處理當前節點,再轉左
  • 時間複雜度: O(n)
  • 空間複雜度: O(h)
class Solution {
    fun convertBST(root: TreeNode?): TreeNode? {
        val stack = ArrayDeque<TreeNode>()
        var sum = 0
        var current = root
        while (current != null || stack.isNotEmpty()) {
            while (current != null) {
                stack.addLast(current)
                current = current.right
            }
            current = stack.removeLast()
            sum += current.`val`
            current.`val` = sum
            current = current.left
        }
        return root
    }
}
Morris 遍歷法(O(1) 空間)

可以用 Morris 遍歷達到 O(1) 空間,但程式碼較複雜,面試中反向中序遍歷已經足夠。Morris 遍歷利用葉節點的空指標建立臨時鏈接,省去堆疊的空間開銷。

🔑 Takeaways#

  • Pattern: 反向中序遍歷(右 -> 根 -> 左)是 BST 從大到小遍歷的標準方式
  • 關鍵技巧: 將中序遍歷的方向反轉(左右互換),配合累加變數即可完成 Greater Tree 轉換