Description

給定一棵二元樹,求其直徑。直徑定義為任意兩個節點之間最長路徑的邊數。此路徑不一定經過根節點。

Example:

Input: root = [1,2,3,4,5] Output: 3 (路徑為 4 -> 2 -> 1 -> 3 或 5 -> 2 -> 1 -> 3)

Intuition#

對每個節點,經過它的最長路徑 = 左子樹高度 + 右子樹高度;全域追蹤最大值。

  • 直徑不一定經過根節點,所以要檢查每個節點
  • 在計算高度的同時順便更新直徑,一次 DFS 即可完成

Approaches#

1: Brute Force(每個節點分別計算)#

  • 概念: 對每個節點計算其左右子樹高度之和,取最大值。每次計算高度需要遍歷子樹。
  • 時間複雜度: O(n^2),最差情況下每個節點都要重新計算高度
  • 空間複雜度: O(n)
class Solution {
    fun diameterOfBinaryTree(root: TreeNode?): Int {
        if (root == null) return 0
        val throughRoot = height(root.left) + height(root.right)
        val leftDiameter = diameterOfBinaryTree(root.left)
        val rightDiameter = diameterOfBinaryTree(root.right)
        return maxOf(throughRoot, leftDiameter, rightDiameter)
    }

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

⭐ 2: DFS 同時計算高度與直徑#

  • 概念: 在計算每個節點高度的遞迴過程中,同時更新全域直徑變數
  • 時間複雜度: O(n)
  • 空間複雜度: O(h)
class Solution {
    private var diameter = 0

    fun diameterOfBinaryTree(root: TreeNode?): Int {
        diameter = 0
        height(root)
        return diameter
    }

    private fun height(node: TreeNode?): Int {
        if (node == null) return 0
        val left = height(node.left)
        val right = height(node.right)
        diameter = maxOf(diameter, left + right)
        return 1 + maxOf(left, right)
    }
}

🔑 Takeaways#

  • Pattern: 在遞迴中利用「回傳值」和「全域變數」同時追蹤兩個不同的量
  • 關鍵技巧: 很多樹的最佳化問題(直徑、最大路徑和)都可以在計算高度時順便求解,避免重複遍歷