title: Binary Tree Coloring Game
slug: binary-tree-coloring-game
difficulty: medium
leetcode_id: 1145
leetcode_url: https://leetcode.com/problems/binary-tree-coloring-game/
categories:
  - trees
patterns:
  - dfs
  - tree-traversal

function_signature: "def btree_game_winning_move(root: TreeNode, n: int, x: int) -> bool:"

test_cases:
  visible:
    - input: { root: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], n: 11, x: 3 }
      expected: true
    - input: { root: [1, 2, 3], n: 3, x: 1 }
      expected: false
  hidden:
    - input: { root: [1], n: 1, x: 1 }
      expected: false
    - input: { root: [1, 2, 3, 4, 5, null, null], n: 5, x: 2 }
      expected: true
    - input: { root: [1, 2, 3, 4, 5, 6, 7], n: 7, x: 1 }
      expected: true
    - input: { root: [1, 2, null, 3, null, 4, null, 5], n: 5, x: 3 }
      expected: false
    - input: { root: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], n: 15, x: 4 }
      expected: true
    - input: { root: [1, 2, 3, null, null, 4, 5], n: 5, x: 3 }
      expected: false

description: |
  Two players play a turn-based game on a binary tree. We are given the `root` of this binary tree, and the number of nodes `n` in the tree. `n` is odd, and each node has a distinct value from `1` to `n`.

  Initially, the first player names a value `x` with `1 <= x <= n`, and the second player names a value `y` with `1 <= y <= n` and `y != x`. The first player colours the node with value `x` red, and the second player colours the node with value `y` blue.

  Then, the players take turns starting with the first player. In each turn, that player chooses a node of their colour (red if player 1, blue if player 2) and colours an **uncoloured** neighbour of the chosen node (either the left child, right child, or parent of the chosen node).

  If (and only if) a player cannot choose such a node in this way, they must pass their turn. If both players pass their turn, the game ends, and the winner is the player that coloured more nodes.

  You are the second player. If it is possible to choose such a `y` to ensure you win the game, return `true`. If it is not possible, return `false`.

constraints: |
  - The number of nodes in the tree is `n`
  - `1 <= x <= n <= 100`
  - `n` is odd
  - `1 <= Node.val <= n`
  - All the values of the tree are **unique**

examples:
  - input: "root = [1,2,3,4,5,6,7,8,9,10,11], n = 11, x = 3"
    output: "true"
    explanation: "The second player can choose the node with value 2."
  - input: "root = [1,2,3], n = 3, x = 1"
    output: "false"
    explanation: "No matter which node the second player chooses, the first player will win."

explanation:
  intuition: |
    Imagine the binary tree as a network of connected rooms. When the first player colours node `x` red, they effectively "block" that node — splitting the tree into **at most three separate regions**:

    1. The **left subtree** of node `x`
    2. The **right subtree** of node `x`
    3. The **parent region** (everything connected through `x`'s parent)

    Think of it like placing a roadblock at node `x`. Once placed, you can no longer freely move between the three regions — they're isolated from each other.

    The key insight is that as the second player, your optimal strategy is to **claim one entire region by colouring a node adjacent to `x`**. If you colour `x`'s left child, you get the entire left subtree. If you colour `x`'s right child, you get the entire right subtree. If you colour `x`'s parent, you get everything *except* `x`'s subtree.

    Since each player expands from their starting node to uncoloured neighbours, once you claim a region adjacent to `x`, the first player can never enter it — they're blocked by their own red node!

    You win if any of these three regions contains **more than half** the nodes (i.e., more than `n / 2`). The game is about counting: find the size of each region and check if any exceeds `n / 2`.

  approach: |
    We solve this by counting the nodes in each of the three regions created when player 1 chooses node `x`:

    **Step 1: Find node `x` in the tree**

    - Use DFS to locate the node with value `x`
    - This is our "pivot point" that divides the tree

    &nbsp;

    **Step 2: Count nodes in the left and right subtrees of `x`**

    - `left_count`: Number of nodes in `x`'s left subtree
    - `right_count`: Number of nodes in `x`'s right subtree
    - Use a recursive helper function to count nodes in any subtree

    &nbsp;

    **Step 3: Calculate the parent region size**

    - The parent region contains all nodes *not* in `x`'s subtree
    - `parent_count = n - left_count - right_count - 1` (subtract 1 for node `x` itself)

    &nbsp;

    **Step 4: Check if any region exceeds half**

    - We win if `left_count > n / 2` OR `right_count > n / 2` OR `parent_count > n / 2`
    - Since `n` is odd, `n / 2` uses integer division, so we need strictly more than `n // 2` nodes

    &nbsp;

    The beauty of this approach is that we don't need to simulate the game — we just need to recognise that the optimal second move is always adjacent to `x`, and then count.

  common_pitfalls:
    - title: Simulating the Entire Game
      description: |
        A tempting approach is to simulate every possible game state: try each choice of `y`, then simulate alternating turns to see who wins.

        This is massively overcomplicated. The game has a simple structure: once both players choose their starting nodes, the outcome is deterministic based on region sizes. There's no need for game simulation, minimax, or dynamic programming.

        The insight that player 2 should always choose a neighbour of `x` reduces this from a complex game tree to a simple counting problem.
      wrong_approach: "Simulating turn-by-turn gameplay"
      correct_approach: "Count nodes in three regions and compare to n/2"

    - title: Forgetting the Parent Region
      description: |
        It's easy to only consider the left and right subtrees of `x`, forgetting that player 2 could choose `x`'s parent.

        If `x` is deep in the tree with small subtrees, the parent region (everything except `x`'s subtree) could be huge. For example, if `x` is a leaf node, its parent region contains `n - 1` nodes!

        Always calculate all three regions: left subtree, right subtree, and parent region.
      wrong_approach: "Only checking left and right subtree sizes"
      correct_approach: "Check all three regions including parent"

    - title: Off-by-One in Parent Count
      description: |
        When calculating the parent region size, remember to subtract 1 for node `x` itself:

        `parent_count = n - left_count - right_count - 1`

        The parent region contains all nodes except `x` and its subtrees. Forgetting the `-1` overcounts by including `x` in the parent region.
      wrong_approach: "parent_count = n - left_count - right_count"
      correct_approach: "parent_count = n - left_count - right_count - 1"

  key_takeaways:
    - "**Game theory simplification**: Complex games often have simple optimal strategies. Here, player 2's best move is always adjacent to `x`"
    - "**Tree partitioning**: Removing a node from a tree creates independent subtrees. Count the sizes to analyse connectivity"
    - "**The majority principle**: In a zero-sum game with `n` items, controlling more than `n/2` guarantees victory"
    - "**DFS for tree metrics**: Recursive subtree counting is a fundamental tree operation — master it for many tree problems"

  time_complexity: "O(n). We traverse the tree once to find node `x` and count subtree sizes."
  space_complexity: "O(h) where `h` is the height of the tree. This is the recursion stack depth, which is O(log n) for balanced trees and O(n) for skewed trees."

solutions:
  - approach_name: DFS Subtree Counting
    is_optimal: true
    code: |
      class TreeNode:
          def __init__(self, val=0, left=None, right=None):
              self.val = val
              self.left = left
              self.right = right

      def btree_game_winning_move(root: TreeNode, n: int, x: int) -> bool:
          left_count = 0
          right_count = 0

          def count_nodes(node: TreeNode) -> int:
              """Count total nodes in a subtree."""
              if not node:
                  return 0
              return 1 + count_nodes(node.left) + count_nodes(node.right)

          def find_x(node: TreeNode) -> bool:
              """Find node x and count its left/right subtrees."""
              nonlocal left_count, right_count

              if not node:
                  return False

              if node.val == x:
                  # Found x! Count its children's subtrees
                  left_count = count_nodes(node.left)
                  right_count = count_nodes(node.right)
                  return True

              # Keep searching in left and right subtrees
              return find_x(node.left) or find_x(node.right)

          # Find node x and get subtree counts
          find_x(root)

          # Calculate parent region size
          # (all nodes not in x's subtree, excluding x itself)
          parent_count = n - left_count - right_count - 1

          # Player 2 wins if any region has more than half the nodes
          half = n // 2
          return left_count > half or right_count > half or parent_count > half
    explanation: |
      **Time Complexity:** O(n) — We visit each node at most twice: once to find `x`, once to count subtrees.

      **Space Complexity:** O(h) — Recursion stack depth equals tree height.

      We use DFS to find node `x` and count its left and right subtree sizes. The parent region is calculated by subtraction. If any region exceeds `n // 2` nodes, player 2 can win by choosing a node adjacent to `x` in that region.

  - approach_name: Single DFS Pass
    is_optimal: true
    code: |
      def btree_game_winning_move(root: TreeNode, n: int, x: int) -> bool:
          left_count = 0
          right_count = 0

          def dfs(node: TreeNode) -> int:
              """Count nodes in subtree, capturing x's children counts."""
              nonlocal left_count, right_count

              if not node:
                  return 0

              # Recursively count left and right subtrees
              left = dfs(node.left)
              right = dfs(node.right)

              # If this is node x, save the subtree counts
              if node.val == x:
                  left_count = left
                  right_count = right

              return 1 + left + right

          # Single DFS traversal
          dfs(root)

          # Check if player 2 can claim a majority region
          parent_count = n - left_count - right_count - 1
          half = n // 2

          return max(left_count, right_count, parent_count) > half
    explanation: |
      **Time Complexity:** O(n) — Single traversal visiting each node exactly once.

      **Space Complexity:** O(h) — Recursion stack depth.

      This optimised version uses a single DFS pass. As we return from each recursive call, we check if the current node is `x` and capture its subtree sizes. This avoids the separate `find_x` and `count_nodes` functions, making the code more elegant.
