↓快轉到主要內容
  1. LeetCode/

LeetCode 968: Binary Tree Cameras

·1 分鐘· ·
LeetCode Hard Dynamic-Programming Tree Depth-First-Search Binary-Tree Dp-on-Trees
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2026-10-30

  • 提交紀錄:Wrong Answer ×3 → Accepted → Wrong Answer ×2 → Accepted

解題思路
#

貪心的直覺:葉子不放相機,放在葉子的父節點比較划算,因為父節點的相機能同時蓋到葉子、父節點自己和祖父。所以由下往上(後序,Tree 模板七)決定,每個節點回傳三種狀態之一:

狀態意思
有相機自己放了相機
被覆蓋沒放相機,但被小孩的相機蓋到
沒被覆蓋交給父節點處理

規則,順序很重要:

  1. 有任一個小孩沒被覆蓋:自己一定要放相機。
  2. 否則有任一個小孩有相機:自己被覆蓋。
  3. 否則:自己沒被覆蓋,交給父節點。

空節點當成「被覆蓋」,這樣葉子會回傳「沒被覆蓋」,相機就放在它的父節點。最後根如果沒被覆蓋,沒有父節點可以幫忙,要補一台。

我的嘗試
#

錯了五次:

  1. 只用 True / False 兩種狀態(需要被覆蓋 / 不需要)。這樣「被小孩的相機蓋到」和「沒被覆蓋」混在一起,根沒被覆蓋時也沒補:[0] 回傳 0,答案是 1。第二次交了幾乎一樣的程式碼。
  2. 加上「根是葉子就加一」的特例:[0,0,0,0,null,0,0,0] 回傳 3,答案是 2。問題是兩種狀態不夠,特例補不完。
  3. 改成三種狀態就過了。
  4. 之後再重寫一次:規則對了,但忘了最後補根的相機([0] 回傳 0);接著把補根的條件寫成檢查「被覆蓋」,應該是檢查「沒被覆蓋」。修正之後過了。

正確解法
#

class Solution:
    def minCameraCover(self, root: Optional[TreeNode]) -> int:
        CAMERA, COVERED, NOT_COVERED = 0, 1, 2
        self.count = 0

        def dfs(node):
            if not node:
                return COVERED
            left = dfs(node.left)
            right = dfs(node.right)
            if left == NOT_COVERED or right == NOT_COVERED:
                self.count += 1
                return CAMERA
            if left == CAMERA or right == CAMERA:
                return COVERED
            return NOT_COVERED

        if dfs(root) == NOT_COVERED:
            self.count += 1
        return self.count

我當時是回傳三個布林值的 tuple,意思一樣,用一個狀態碼比較好讀。

複雜度
#

時間 O(n),空間 O(h)。

要特別避免的錯誤
#

  • 兩種狀態不夠,「被覆蓋但沒相機」和「沒被覆蓋」必須分開。
  • 空節點要當成「被覆蓋」,不然葉子會被迫放相機。
  • 規則 1 要先檢查:小孩沒被覆蓋時,就算另一個小孩有相機,自己也一定要放。
  • 最後根沒被覆蓋要補一台。

面試口說整理
#

我用後序貪心,每個節點回傳三種狀態:有相機、被覆蓋、沒被覆蓋。空節點算被覆蓋,所以葉子不放相機,交給父節點。有小孩沒被覆蓋就放相機;否則有小孩有相機就算被覆蓋;都不是就交給父節點。最後根沒被覆蓋要補一台。時間 O(n)。

相關文章

LeetCode 124: Binary Tree Maximum Path Sum
·1 分鐘
LeetCode Hard Dynamic-Programming Tree Depth-First-Search Binary-Tree Dp-on-Trees
LeetCode 124 解題筆記:DFS 回傳往下的一條鏈,在每個節點用「左鏈 + 自己 + 右鏈」更新答案;負的鏈要丟掉
LeetCode 1372: Longest ZigZag Path in a Binary Tree
·1 分鐘
LeetCode Medium Dynamic-Programming Tree Depth-First-Search Binary-Tree Dp-on-Trees
LeetCode 1372 解題筆記:每個節點回傳兩個值:從這裡先往左、先往右的最長 zigzag;往左走接的是左小孩「先往右」的值
LeetCode 297: Serialize and Deserialize Binary Tree
·1 分鐘
LeetCode Hard String Tree Depth-First-Search Breadth-First-Search Design Binary-Tree
LeetCode 297 解題筆記:前序遍歷加上空節點標記就能唯一還原樹;反序列化用迭代器依序取值
LeetCode 687: Longest Univalue Path
·1 分鐘
LeetCode Medium Tree Depth-First-Search Binary-Tree Dp-on-Trees
LeetCode 687 解題筆記:DFS 回傳往下同值的最長鏈(邊數),在每個節點用左 + 右更新答案
LeetCode 2973: Find Number of Coins to Place in Tree Nodes
·2 分鐘
LeetCode Hard Dynamic-Programming Tree-Dp Depth-First-Search Sorting Heap Tree
LeetCode 2973 解題筆記:三個數的最大乘積只可能是「最大的三個」或「最小的兩個 × 最大的一個」;每個子樹只回傳最小 2 個和最大 3 個,後序合併
LeetCode 1373: Maximum Sum BST in Binary Tree
·1 分鐘
LeetCode Hard Dynamic-Programming Tree-Dp Binary-Search-Tree Depth-First-Search Tree
LeetCode 1373 解題筆記:後序遍歷回傳 (總和, 最小值, 最大值, 是不是 BST);左子樹的最大值 < 自己 < 右子樹的最小值才是 BST