3 minute read

Problem Statement

problem-2641

Intuition

The problem asks us to replace each node’s value with the sum of all its cousins’ values. Cousins are nodes at the same level of the tree, but with different parents.

Initially, the value of each node is independent of its cousin’s value, but we need to collect the total value of all cousin nodes and replace each node’s value with that sum.

Approach

  1. Level-order traversal: We can traverse the tree level by level using a queue (Breadth-First Search - BFS). This ensures we only look at nodes at the same level.
  2. Track parents: For each node, we maintain its parent. This helps in identifying which nodes are siblings, so we can exclude them from the sum of cousins.
  3. Calculate the cousin sum: For each node at a given level, compute the total sum of values for all nodes, and then subtract the sum of values of the siblings for each node. This gives us the sum of the cousins.
  4. Update values: After calculating the cousin sum, we update each node’s value accordingly.

Complexity

  • Time complexity:
    Since we are traversing all the nodes in the tree once, the time complexity is \(O(n)\) where \(n\) is the number of nodes in the tree.

  • Space complexity:
    The space complexity is also \(O(n)\) because we store the nodes in a queue and maintain dictionaries for sums and parent-child relationships.

Code

# 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 replaceValueInTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        queue = deque()
        queue.append([root, None])
        while queue:
            n = len(queue)
            curr_sum = defaultdict(int)
            parents = defaultdict(list)
            total_sum = 0
            for _ in range(n):
                node, parent = queue.popleft()
                curr_sum[parent] += node.val
                parents[parent].append(node)
                total_sum += node.val
                if node:
                    if node.left:
                        queue.append([node.left, node])
                    if node.right:
                        queue.append([node.right, node])
            for k, v in curr_sum.items():
                sum_all_cousins = total_sum - v
                for node in parents[k]:
                    node.val = sum_all_cousins
        return root

Editorial

Approach 1: Two Pass BFS

class Solution:
    def replaceValueInTree(self, root):
        if not root:
            return root
        node_queue = deque([root])
        level_sums = []

        # First BFS: Calculate sum of nodes at each level
        while node_queue:
            level_sum = 0
            level_size = len(node_queue)
            for _ in range(level_size):
                current_node = node_queue.popleft()
                level_sum += current_node.val
                if current_node.left:
                    node_queue.append(current_node.left)
                if current_node.right:
                    node_queue.append(current_node.right)
            level_sums.append(level_sum)

        # Second BFS: Update each node's value to sum of its cousins
        node_queue.append(root)
        level_index = 1
        root.val = 0  # Root has no cousins
        while node_queue:
            level_size = len(node_queue)
            for _ in range(level_size):
                current_node = node_queue.popleft()

                sibling_sum = (
                    current_node.left.val if current_node.left else 0
                ) + (current_node.right.val if current_node.right else 0)

                if current_node.left:
                    current_node.left.val = (
                        level_sums[level_index] - sibling_sum
                    )
                    node_queue.append(current_node.left)
                if current_node.right:
                    current_node.right.val = (
                        level_sums[level_index] - sibling_sum
                    )
                    node_queue.append(current_node.right)
            level_index += 1

        return root

Approach 2: Two Pass DFS

class Solution:
    def __init__(self):
        self.level_sums = [0] * 100000

    def replaceValueInTree(self, root):
        self._calculate_level_sum(root, 0)
        self.replace_value_in_tree_internal(root, 0, 0)
        return root

    def _calculate_level_sum(self, node, level):
        if node is None:
            return
        self.level_sums[level] += node.val
        self._calculate_level_sum(node.left, level + 1)
        self._calculate_level_sum(node.right, level + 1)

    def replace_value_in_tree_internal(self, node, sibling_sum, level):
        if node is None:
            return
        left_child_val = 0 if node.left is None else node.left.val
        right_child_val = 0 if node.right is None else node.right.val

        if level == 0 or level == 1:
            node.val = 0
        else:
            node.val = self.level_sums[level] - node.val - sibling_sum
        self.replace_value_in_tree_internal(
            node.left, right_child_val, level + 1
        )
        self.replace_value_in_tree_internal(
            node.right, left_child_val, level + 1
        )

Approach 3: Single BFS with Running Sum

class Solution:
    def replaceValueInTree(self, root):
        if root is None:
            return root
        node_queue = deque()
        node_queue.append(root)
        previous_level_sum = root.val

        while node_queue:
            level_size = len(node_queue)
            current_level_sum = 0

            for _ in range(level_size):
                current_node = node_queue.popleft()
                # Update node value to cousin sum
                current_node.val = previous_level_sum - current_node.val

                # Calculate sibling sum
                sibling_sum = (
                    0 if current_node.left is None else current_node.left.val
                ) + (
                    0 if current_node.right is None else current_node.right.val
                )

                if current_node.left is not None:
                    current_level_sum += (
                        current_node.left.val
                    )  # Accumulate current level sum
                    current_node.left.val = (
                        sibling_sum  # Update left child's value
                    )
                    node_queue.append(
                        current_node.left
                    )  # Add to queue for next level
                if current_node.right is not None:
                    current_level_sum += (
                        current_node.right.val
                    )  # Accumulate current level sum
                    current_node.right.val = (
                        sibling_sum  # Update right child's value
                    )
                    node_queue.append(
                        current_node.right
                    )  # Add to queue for next level
            previous_level_sum = current_level_sum  # Update previous level sum for next iteration
        return root
  • time: O(n)
  • space: O(n)