Description

滿二元樹是每個節點要嘛有 0 個子節點、要嘛有 2 個子節點的二元樹。給定整數 n,回傳所有具有 n 個節點的滿二元樹。每棵樹的節點值皆為 0。

Example:

Input: n = 7 Output: [[0,0,0,null,null,0,0,null,null,0,0],[0,0,0,null,null,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,null,null,null,null,0,0],[0,0,0,0,0,null,null,0,0]]

Intuition#

n 必須是奇數才可能形成滿二元樹;根用 1 個節點,剩餘 n-1 個分配給左右子樹。

  • 滿二元樹的節點數一定是奇數(每次增加 2 個子節點)
  • 根佔 1 個節點,剩下 n-1 個要分成兩組(各為奇數)給左右子樹
  • 左子樹 i 個,右子樹 n-1-i 個,i 從 1 到 n-2(步長 2)

Approaches#

1: 遞迴#

  • 概念: 枚舉左子樹的節點數,遞迴生成左右子樹,交叉組合
  • 時間複雜度: O(2^(n/2))
  • 空間複雜度: O(n * 2^(n/2))
class Solution {
    fun allPossibleFBT(n: Int): List<TreeNode?> {
        if (n % 2 == 0) return emptyList()
        if (n == 1) return listOf(TreeNode(0))

        val result = mutableListOf<TreeNode?>()
        for (leftCount in 1 until n step 2) {
            val rightCount = n - 1 - leftCount
            val leftTrees = allPossibleFBT(leftCount)
            val rightTrees = allPossibleFBT(rightCount)
            for (left in leftTrees) {
                for (right in rightTrees) {
                    val root = TreeNode(0)
                    root.left = left
                    root.right = right
                    result.add(root)
                }
            }
        }
        return result
    }
}

⭐ 2: 遞迴 + Memoization#

  • 概念: 用 HashMap 快取不同 n 值的結果,避免重複計算
  • 時間複雜度: O(2^(n/2))
  • 空間複雜度: O(n * 2^(n/2))
class Solution {
    private val memo = HashMap<Int, List<TreeNode?>>()

    fun allPossibleFBT(n: Int): List<TreeNode?> {
        if (n % 2 == 0) return emptyList()
        if (n == 1) return listOf(TreeNode(0))
        memo[n]?.let { return it }

        val result = mutableListOf<TreeNode?>()
        for (leftCount in 1 until n step 2) {
            val rightCount = n - 1 - leftCount
            val leftTrees = allPossibleFBT(leftCount)
            val rightTrees = allPossibleFBT(rightCount)
            for (left in leftTrees) {
                for (right in rightTrees) {
                    val root = TreeNode(0)
                    root.left = left
                    root.right = right
                    result.add(root)
                }
            }
        }
        memo[n] = result
        return result
    }
}

🔑 Takeaways#

  • Pattern: 與 95 題(Unique BST II)結構相似,都是枚舉分割點 + 遞迴生成 + 交叉組合
  • 關鍵技巧: 利用「滿二元樹節點數必為奇數」快速剪枝,步長為 2 減少一半的枚舉