Cousins in Binary Tree

Updated on 19 May, 2025
Cousins in Binary Tree header image

Problem Statement

In this task, we are given the root of a binary tree where each node has unique values, along with two specific values x and y representing different nodes in the tree. We must determine if the nodes corresponding to these values are "cousins". For clarification, cousins in a binary tree are defined as nodes that are at the same depth (or level) but do not share the same parent. The task involves checking the relationship between the nodes corresponding to x and y according to these definitions and returning true if they are cousins, otherwise returning false.

Examples

Example 1

Input:

root = [1,2,3,4], x = 4, y = 3

Output:

false

Example 2

Input:

root = [1,2,3,null,4,null,5], x = 5, y = 4

Output:

true

Example 3

Input:

root = [1,2,3,null,4], x = 2, y = 3

Output:

false

Constraints

  • The number of nodes in the tree is in the range [2, 100].
  • 1 <= Node.val <= 100
  • Each node has a unique value.
  • x != y
  • x and y are exist in the tree.

Approach and Intuition

  • To solve the problem, we need to determine if the nodes x and y are at the same depth yet have different parents. This leads us to using a breadth-first search (BFS), which provides a systematic method for exploring the tree level by level.

    1. Initiate a queue that will include nodes alongside their corresponding depth and their parent information. Start with the root node having depth 0 and no parent (null).

    2. Process the nodes in the queue:

      • For each node, check its children.
      • If a child is either x or y, store its depth and its parent.
      • Put the child into the queue with incremented depth and current node as the parent.
    3. After populating the parent and depth information for both x and y (or determining that one/both are absent in the tree), compare their depths and parents:

      • Cousins are identified by having the same depth but different parents.
  • Given the constraints:

    • Since each node value must be unique, there's no ambiguity in node identification.
    • The tree size range ([2, 100]) ensures our BFS approach remains efficient.
    • Verification includes checking if both x and y actually exist in the tree as given by the constraint x and y are exist in the tree.
  • Using the examples provided:

    • The first example, with nodes [1,2,3,4] and x = 4, y = 3, shows x and y at different depths, resulting in false.
    • In the second example, in the tree [1,2,3,null,4,null,5], x = 5 and y = 4 are at the same depth and do not share the same parent, thus true.
    • The third example shows a sibling relationship (x = 2 and y = 3 share the same parent), which mean they are not cousins, hence false.

Through following this approach and set of checks, one can effectively and accurately determine cousin relationships in a binary tree as defined.

Solutions

  • Java
  • Python
java
class Solution {

    public boolean checkCousins(TreeNode root, int x, int y) {
        // Use queue for level order traversal
        Queue<TreeNode> q = new LinkedList<>();
        q.add(root);
        
        while (!q.isEmpty()) {
            boolean isSibling = false;
            boolean isCousin = false;

            int levelSize = q.size();

            for (int i = 0; i < levelSize; ++i) {
                TreeNode current = q.poll();

                if (current == null) isSibling = false; // Reset sibling status at each boundary.
                else {
                    if (current.val == x || current.val == y) {
                        if (!isCousin) {
                            isSibling = isCousin = true;
                        } else {
                            return !isSibling;
                        }
                    }

                    if (current.left != null) q.add(current.left);
                    if (current.right != null) q.add(current.right);
                    q.add(null); // Define a boundary for siblings.
                }
            }
            if (isCousin) return false; // Found only one node of interest.
        }
        return false;
    }
}

This Java solution checks whether two nodes in a binary tree are cousins. Cousins are nodes that are on the same level but have different parents. The method checkCousins utilizes level order traversal (using breadth-first search) to explore the tree and compare nodes.

  • Start by initializing a queue and adding the root node to it.
  • Continue with a loop that processes nodes until the queue is empty.
  • At each level of the tree:
    • Initialize flags, isSibling and isCousin, to manage sibling status and cousin detection.
    • Determine the number of nodes at the current level with levelSize, indicating how many nodes to process for this iteration.
    • Loop through each node at the current level:
      • Poll the node from the queue to process it.
      • If the node is null, reset the isSibling flag. A null node acts as a marker to differentiate potential sibling groups.
      • Identify if the current node’s value matches either of the values being checked (x or y):
        • If one match is found and isCousin is false, set isSibling and isCousin to true.
        • If a second match is found and isSibling is true, return !isSibling to confirm they are cousins (since they shouldn’t be siblings).
      • Push the left and right children of the current node into the queue.
      • Push null into the queue after adding children to maintain sibling boundaries.
    • If only one of the nodes (either x or y) is found (isCousin is true) and there was no sibling matched (isSibling is false), continue to the next level.
  • Finally, if no companionship have been determined by the end of the loop, return false as the nodes are neither at the same level nor disconnected from having the same parent.

This method ensures that you efficiently identify if two nodes are cousins by maintaining clear control of level boundaries and sibling relationships.

python
from collections import deque

class Solution:

    def checkCousins(self, root: TreeNode, p: int, q: int) -> bool:

        # Initialize BFS queue
        bfs_queue = deque([root])

        while bfs_queue:

            at_same_level = False
            found_cousins = False
            level_size = len(bfs_queue)
            for _ in range(level_size):

                # Dequeue element
                curr_node = bfs_queue.popleft()  

                # Check if it's None that marks different parent boundary
                if curr_node is None:
                    at_same_level = False
                else:
                    # Check if current is either p or q
                    if curr_node.val == p or curr_node.val == q:
                        # If first of p or q is found at this level
                        if not found_cousins:
                            at_same_level, found_cousins = True, True
                        else:
                            # if they are still within the same parent boundary
                            return not at_same_level

                    # Continue BFS with children
                    if curr_node.left:
                        bfs_queue.append(curr_node.left)
                    if curr_node.right:
                        bfs_queue.append(curr_node.right)
                    # Marker for different parents
                    bfs_queue.append(None)
                    
            # Only one of p or q found at this level
            if found_cousins:
                return False

        return False

In the provided Python code, you can determine if two nodes in a binary tree are cousins. The code employs a Breadth-First Search (BFS) strategy using a queue to traverse the tree level by level.

  • Begin by initializing a deque to facilitate the BFS, starting with the root node.
  • Use a while loop to process nodes until the queue is empty. For each level of the BFS:
    • Initialize flags at_same_level to monitor if a found node's sibling might be the cousin and found_cousins to check if one of the cousin candidates was found at the current level.
    • Determine level_size as the count of nodes at the current tree level.
    • Iterate over each node of the current level:
      • Dequeue the front node.
      • Check if it is None which indicates the boundary between different parents within the same level.
      • Validate if the current node matches one of the target values (p or q). If a match is found:
        • Set found_cousins to True. If another target is found later in the same breadth level but before the next None, they are cousins from different parents.
        • Check at_same_level to return a negative result if another target value is found after another sibling - indicating they are siblings instead of cousins.
      • If the current node has children, enqueue them and place None as a marker for potential different parent boundaries.
    • If after scanning a level only one target is found, determine that the other must be at a different level, thus not making them cousins, and return False.
  • Execute BFS until all levels are visited. If no conclusive results emerge during the traversal, return False.

This Python code ensures an efficient check on whether two nodes are cousins in a binary tree without revisiting nodes or retaining unnecessary data, making use of the characteristics of BFS and control flags to ascertain cousin relation based on tree levels and parent boundaries.

Comments

No comments yet.