Binary Tree Cameras - Solution
Solutions and explanations

Video Explanation

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def minCameraCover(self, root: Optional[TreeNode]) -> int:
        # Status Constants
        HAS_CAM = 1     # node has a camera
        COVERED = 2     # node is covered (by child)
        NEEDS_COV = 3   # node is not covered

        cameras = 0  # camera counter

        # Greedy Post order DFS
        def dfs(node) -> int:
            nonlocal cameras

            # Null nodes are automatically covered
            if not node:
                return COVERED

            # Post-order traversal- so process children first
            left_status = dfs(node.left)
            right_status = dfs(node.right)

            # If any child needs a camera, then place camera at this node
            if left_status == NEEDS_COV or right_status == NEEDS_COV:
                cameras += 1
                return HAS_CAM

            # If any child has a camera, then this node is already covered
            if left_status == HAS_CAM or right_status == HAS_CAM:
                return COVERED
            
            # Otherwise, node is not covered
            return NEEDS_COV
        
        return cameras + 1 if dfs(root) == NEEDS_COV else cameras

Complexity Analysis

Here, n is the number of nodes, and h is the height of the binary tree.

  • Time Complexity: O(n)
    • Each node is visited during traversal.
  • Space Complexity: O(h)
    • The recursion stack takes O(h) extra space.
      • In the best case (a balanced tree), h = log n.
      • In the worst case (a skewed tree), h = n, so the recursion stack can use atmost O(n) space.