[Easy] 993. Cousins in Binary Tree
Given the root of a binary tree with unique values and the values of two different nodes of the tree x and y, return true if the nodes corresponding to the values x and y are cousins, or false otherwise.
Two nodes of a binary tree are cousins if they have the same depth with different parents.
Note that in a binary tree, the root node is at depth 0, and children of each depth k node are at depth k + 1.
Examples
Example 1:
Input: root = [1,2,3,4], x = 4, y = 3
Output: false
Explanation: Nodes 4 and 3 are at the same depth but have the same parent (node 2).
Example 2:
Input: root = [1,2,3,null,4,null,5], x = 5, y = 4
Output: true
Explanation: Nodes 5 and 4 are at the same depth and have different parents.
Example 3:
Input: root = [1,2,3,null,4], x = 2, y = 3
Output: false
Explanation: Nodes 2 and 3 are siblings (same parent), not cousins.
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 != yxandyare guaranteed to exist in the tree.
Thinking Process
- Cousins = Same Depth + Different Parents: Both conditions must be satisfied
- Trees have no cycles — recursion is natural.
- Combine results from left and right subtrees at each node.
- Base case is usually
null; height drives stack space.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Queue BFS (this problem) | O(n) | O(n) | Shortest path in unweighted graphs |
| Multi-source BFS | O(n) | O(n) | Start from all sources simultaneously |
| 0-1 BFS / deque | O(n) | O(n) | Weights 0 or 1 |
| Level-order BFS | O(n) | O(w) | Process by depth/layer |
Solution
Solution: BFS with Parent Tracking
from collections import deque
# 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 isCousins(self, root, x, y):
if not root:
return False
q = deque()
q.append((root, None))
xParent = None
yParent = None
depth = 0
while q:
size = len(q)
for i in range(size):
node, parent = q.popleft()
if node.val == x:
xParent = parent
if node.val == y:
yParent = parent
if node.left:
q.append((node.left, node))
if node.right:
q.append((node.right, node))
if xParent and yParent:
break
depth += 1
return xParent != yParent and xParent is not None and yParent is not None
Solution Explanation
Approach: Queue BFS (this problem)
Key idea: 1. Cousins = Same Depth + Different Parents: Both conditions must be satisfied
How the code works:
- Cousins = Same Depth + Different Parents: Both conditions must be satisfied
- Trees have no cycles — recursion is natural.
- Combine results from left and right subtrees at each node.
- Base case is usually
null; height drives stack space.
Walkthrough — input root = [1,2,3,4], x = 4, y = 3, expected output false:
Nodes 4 and 3 are at the same depth but have the same parent (node 2).
Algorithm Explanation:
- Initialize (Lines 4-7):
- Return false if root is null
- Create queue storing
(node, parent)pairs - Push root with
nullptras parent (root has no parent) - Initialize tracking variables:
xParent,yParent,xDepth,yDepth,depth
- Level Processing (Lines 8-26):
- For each level:
- Get level size before processing
- Process each node at current level (Lines 11-22):
- Extract node and parent from queue
- If node is x: Store its parent and depth
- If node is y: Store its parent and depth
- Add children: Push left and right children with current node as parent
- Early termination (Line 24): If both nodes found, break early
- Increment depth (Line 25): Move to next level
- For each level:
- Check Cousins Condition (Line 27):
- Return
trueif:xDepth == yDepth(same depth) ANDxParent != yParent(different parents)
- Return
Why This Works:
- BFS ensures same level: All nodes at the same level are processed together
- Parent tracking: Storing parent with each node allows us to check if parents differ
- Early termination: Once both nodes are found, we can stop searching
- Depth tracking: Incrementing depth after each level ensures correct depth calculation
Example Walkthrough:
For root = [1,2,3,null,4,null,5], x = 5, y = 4:
Tree structure:
1
/ \
2 3
\ \
4 5
Initial: q = [(1, null)], xParent = null, yParent = null, depth = 0
Level 0 (depth = 0):
size = 1
Process: [(1, null)]
- node = 1, parent = null
- Not x or y
- Add children: (2, 1), (3, 1)
q = [(2, 1), (3, 1)]
depth = 1
Level 1 (depth = 1):
size = 2
Process: [(2, 1), (3, 1)]
- node = 2, parent = 1
- Not x or y
- Add child: (4, 2)
- node = 3, parent = 1
- Not x or y
- Add child: (5, 3)
q = [(4, 2), (5, 3)]
depth = 2
Level 2 (depth = 2):
size = 2
Process: [(4, 2), (5, 3)]
- node = 4, parent = 2
- Found y! yParent = 2, yDepth = 2
- node = 5, parent = 3
- Found x! xParent = 3, xDepth = 2
Both found: xParent && yParent = true, break
Check: xDepth == yDepth? 2 == 2? ✓
xParent != yParent? 3 != 2? ✓
Result: true (they are cousins)
Complexity Analysis:
- Time Complexity: O(n) where n is the number of nodes
- Each node is visited at most once
- Early termination when both nodes are found
- Space Complexity: O(n) for the queue
- Queue stores at most one level of nodes (maximum width of tree)
- O(1) extra space for tracking variables
Edge Cases
- Root is one of the nodes: Root has no parent (nullptr), so it can’t be cousin with any other node
- Siblings: Same parent but same depth - not cousins
- Different depths: Different parents but different depths - not cousins
- One node not found: Shouldn’t happen per constraints, but handled by initialization
Related Problems
- LC 102: Binary Tree Level Order Traversal - Level order traversal
- LC 863: All Nodes Distance K in Binary Tree - Find nodes at distance k
- LC 236: Lowest Common Ancestor of a Binary Tree - Find LCA
Common Mistakes
- Skipping edge cases (empty input, single element, boundaries).
- Off-by-one errors in loops and index ranges.
- Forgetting to handle the case when no valid answer exists.
Key Takeaways
- Cousins = Same Depth + Different Parents: Both conditions must be satisfied
- BFS for Level Tracking: BFS naturally processes nodes level by level
- Parent Tracking: Store parent with each node to compare later
- Early Termination: Break once both nodes are found to optimize
References
- LC 993: Cousins in Binary Tree on LeetCode
- LeetCode Discuss — LC 993: Cousins in Binary Tree
- LeetCode Editorial (may require premium)