Cousins in Binary Tree II

Updated on 16 May, 2025
Cousins in Binary Tree II header image

Problem Statement

Given a binary tree where each node represents a unique value, the problem requires transforming the tree such that the value of each node is replaced by the sum of values of its cousins. Cousins in a binary tree are defined as nodes that are at the same level or depth but do not share the same immediate parent. The transformed tree should maintain the same structure, with only the values of the nodes altered based on the described cousin sum criterion.

Examples

Example 1

Input:

root = [5,4,9,1,10,null,7]

Output:

[0,0,0,7,7,null,11]

Explanation:

The diagram above shows the initial binary tree and the binary tree after changing the value of each node.
- Node with value 5 does not have any cousins so its sum is 0.
- Node with value 4 does not have any cousins so its sum is 0.
- Node with value 9 does not have any cousins so its sum is 0.
- Node with value 1 has a cousin with value 7 so its sum is 7.
- Node with value 10 has a cousin with value 7 so its sum is 7.
- Node with value 7 has cousins with values 1 and 10 so its sum is 11.

Example 2

Input:

root = [3,1,2]

Output:

[0,0,0]

Explanation:

The diagram above shows the initial binary tree and the binary tree after changing the value of each node.
- Node with value 3 does not have any cousins so its sum is 0.
- Node with value 1 does not have any cousins so its sum is 0.
- Node with value 2 does not have any cousins so its sum is 0.

Constraints

  • The number of nodes in the tree is in the range [1, 105].
  • 1 <= Node.val <= 104

Approach and Intuition

  1. Traverse the tree to determine the depth and parent of each node using a breadth-first search (BFS) approach, which naturally explores the tree level by level.
  2. As the BFS proceeds, group nodes by their depths in a dictionary where keys are depths and values are lists of nodes (node values and references) at that depth.
  3. Using this structure, calculate the sum of values for nodes at each depth.
  4. For nodes that have siblings (nodes under the same parent), subtract their values from the total sum at that depth to find the sum of cousin values.
  5. Assign the calculated cousin sum to each respective node.

This approach ensures each node is updated correctly based on the sum of its cousins, and it efficiently leverages level-wise traversal to avoid redundant computations. The use of BFS helps manage nodes level by level, which is crucial for distinguishing depths and consequently identifying cousins in a binary tree.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    TreeNode* modifyTreeValues(TreeNode* root) {
        if (!root) {
            return root;
        }

        queue<TreeNode*> treeQueue;
        treeQueue.push(root);
        int sumOfCurrentLevel = root->val;

        while (!treeQueue.empty()) {
            int sizeOfLevel = treeQueue.size();
            int sumOfNextLevel = 0;

            for (int i = 0; i < sizeOfLevel; i++) {
                TreeNode* current = treeQueue.front();
                treeQueue.pop();
                current->val = sumOfCurrentLevel - current->val;

                int sumForChildren = (current->left ? current->left->val : 0) +
                                     (current->right ? current->right->val : 0);

                if (current->left) {
                    sumOfNextLevel += current->left->val;
                    current->left->val = sumForChildren;
                    treeQueue.push(current->left);
                }
                if (current->right) {
                    sumOfNextLevel += current->right->val;
                    current->right->val = sumForChildren;
                    treeQueue.push(current->right);
                }
            }

            sumOfCurrentLevel = sumOfNextLevel;
        }
        return root;
    }
};

The given C++ code implements a method titled modifyTreeValues within the Solution class, which modifies a binary tree's nodes based on the sum of values in their respective levels. The function operates by:

  • Utilizing a queue data structure to perform a level-order traversal on the tree.
  • Substituting each node's value with the difference between the sum of values at the current level and the current node's value, except for leaf nodes.
  • The values of leaf node children are set based on the sum of the node values that would be their siblings in a complete binary tree.

Here's a brief breakdown of the operations inside the function:

  • Initialization: A queue is initialized with the root node, and the sum for the first level is set to the root node’s value.
  • Level Order Traversal: The function enters a loop that runs until there are no nodes left in the queue.
  • Node Processing: For each node in the current level:
    • The node's value is updated.
    • The sums of potential children values are calculated and used to update the children’s actual values.
    • Children nodes are then added to the queue for subsequent processing.
  • Level Sum Update: At the end of each level, the sum for the next level is calculated to prepare for the updates in the following iteration of the loop.

This method ensures that each node's value is related to the sum of values in its level, effectively making the data in the tree interconnected based on the level-wise sums of their nodes. This transformation can be used in various applications where relative node values need to reflect their hierarchy and grouping in the tree structure.

java
class Solution {

    public TreeNode modifyTreeValues(TreeNode rootNode) {
        if (rootNode == null) {
            return rootNode;
        }

        Queue<TreeNode> treeQueue = new LinkedList<>();
        treeQueue.add(rootNode);
        int sumAtCurrentLevel = rootNode.val;

        while (!treeQueue.isEmpty()) {
            int levelNodeCount = treeQueue.size();
            int sumAtNextLevel = 0;

            for (int i = 0; i < levelNodeCount; i++) {
                TreeNode activeNode = treeQueue.poll();
                // Change current node value
                activeNode.val = sumAtCurrentLevel - activeNode.val;

                // Compute the sum of left and right children
                int childrenSum =
                    (activeNode.left != null ? activeNode.left.val : 0) +
                    (activeNode.right != null ? activeNode.right.val : 0);

                if (activeNode.left != null) {
                    sumAtNextLevel += activeNode.left.val;
                    activeNode.left.val = childrenSum;
                    treeQueue.add(activeNode.left);
                }
                if (activeNode.right != null) {
                    sumAtNextLevel += activeNode.right.val;
                    activeNode.right.val = childrenSum;
                    treeQueue.add(activeNode.right);
                }
            }

            sumAtCurrentLevel = sumAtNextLevel;
        }
        return rootNode;
    }
}

This Java solution modifies a binary tree in such a way that each node's value becomes the difference between the sum of values at its level and the node's current value. This task utilizes breadth-first traversal to explore the tree, ensuring nodes are processed level by level.

  • For initiating the process, a Queue (first in, first out structure) holds the nodes as they are processed. The traversal begins with the root node, both inserting it into the queue and initializing the sum of the level with the root node's value.
  • During traversal, the process computes the value for each node as the difference between the current level's sum and the node's value.
  • For each node processed, the sum of values of its left and right children is computed. If they exist, their values are temporarily updated to this sum during the same iteration that they are enqueued for further processing in subsequent levels.
  • The sum of values for the next level is prepared using the sum of values of the children, and this total sum becomes the starting value for the next level processing.

The algorithm repeats until all levels of the tree have been processed, and then the modified root node of the tree is returned, reflecting the updated node values. This method effectively adjusts each node under the given rule, making optimal use of queue operations for level-wise tree traversal. This ensures that all nodes are processed and updated efficiently according to the computed rules for each level, resulting in a dynamically modified tree based on its original structure.

python
class TreeProcessor:
    def updateTreeNodeValues(self, rootNode):
        if rootNode is None:
            return None
        queue = deque()
        queue.append(rootNode)
        level_total = rootNode.val

        while queue:
            nodes_at_this_level = len(queue)
            total_for_next_level = 0

            for _ in range(nodes_at_this_level):
                node = queue.popleft()
                node.val = level_total - node.val

                sum_of_children = (
                    0 if node.left is None else node.left.val
                ) + (
                    0 if node.right is None else node.right.val
                )

                if node.left:
                    total_for_next_level += node.left.val
                    node.left.val = sum_of_children
                    queue.append(node.left)
                if node.right:
                    total_for_next_level += node.right.val
                    node.right.val = sum_of_children
                    queue.append(node.right)

            level_total = total_for_next_level
        return rootNode

The provided Python code defines a class TreeProcessor with a method updateTreeNodeValues that modifies a binary tree based on specific rules. This method takes the root node of a binary tree as an input and updates the value of each node across every level.

Here's a breakdown of how the updateTreeNodeValues method works:

  • Starts by checking if the root node is None, in which case it returns None.
  • Uses a deque for breadth-first search traversal of the tree, initializing with the root node.
  • The initial node value at root level is set as the starting point.
  • For each level in the tree:
    • Captures the number of nodes present at the current level.
    • Initializes a variable for tracking the sum of node values for the next level.
    • Iterates over each node at the current level, updating its value to level_total - node.val.
    • Computes the sum of child node values if they exist and assigns it to appropriate children while updating the total for the next level.
    • Each child node with a new value is added back into the queue for further processing.
  • Updates the level sum with the total sum calculated for the next level.
  • Finally, returns the modified root node.

This method effectively transforms each node's value in the tree based on the cumulative properties of its level, ensuring that each node's new value is linked to the structure and sum of its level's nodes, and paving the way for subsequent transformations as it descends through the tree.

Comments

No comments yet.