A clear explanation of checking whether two binary tree nodes are cousins using BFS with parent tracking.
Problem Restatement
We are given the root of a binary tree with unique values.
We are also given two different values:
x
yBoth values exist in the tree.
We need to return True if the nodes with values x and y are cousins.
Two nodes are cousins if:
| Condition | Meaning |
|---|---|
| Same depth | They are on the same tree level |
| Different parents | They do not share the same immediate parent |
The root has depth 0. Children of a node at depth d have depth d + 1.
Input and Output
| Item | Meaning |
|---|---|
| Input | Root of a binary tree, and two node values x and y |
| Output | Boolean |
| Cousin condition | Same depth, different parents |
| Important detail | Values are unique, and both x and y exist |
Function shape:
def isCousins(root: Optional[TreeNode], x: int, y: int) -> bool:
...Examples
Example 1:
root = [1, 2, 3, 4]
x = 4
y = 3Tree:
1
/ \
2 3
/
4Node 4 has depth 2.
Node 3 has depth 1.
They are not at the same depth.
Answer:
FalseExample 2:
root = [1, 2, 3, None, 4, None, 5]
x = 5
y = 4Tree:
1
/ \
2 3
\ \
4 5Node 4 and node 5 are both at depth 2.
Their parents are different: 2 and 3.
Answer:
TrueExample 3:
root = [1, 2, 3, None, 4]
x = 2
y = 3Node 2 and node 3 are at the same depth, but they have the same parent: node 1.
They are siblings, not cousins.
Answer:
FalseFirst Thought: Find Depth and Parent
To decide whether two nodes are cousins, we need two pieces of information for each target node:
| Value | Needed information |
|---|---|
x | Its depth and parent |
y | Its depth and parent |
After finding both nodes:
same_depth = depth_x == depth_y
different_parent = parent_x != parent_yThe answer is:
same_depth and different_parentWe can collect this information with either DFS or BFS.
Key Insight
BFS is natural because it processes the tree level by level.
At each level:
- Check whether
xappears. - Check whether
yappears. - Record their parents.
- After finishing the level, decide whether they are cousins.
If both are found in the same level, they have the same depth. Then they are cousins only if their parents are different.
If only one is found in a level, the other one must be at a different depth, so they cannot be cousins.
Algorithm
Use a queue containing pairs:
(node, parent)Start with:
(root, None)While the queue is not empty:
- Process exactly one level.
- Set
parent_x = Noneandparent_y = None. - For each node in the current level:
- If
node.val == x, store its parent. - If
node.val == y, store its parent. - Push children into the queue with the current node as parent.
- If
- After the level:
- If both parents were found, return whether they are different.
- If exactly one was found, return
False.
If the loop ends, return False.
Correctness
BFS processes all nodes at depth d before any node at depth d + 1.
During each level, the algorithm records the parent of x if x appears at that depth, and the parent of y if y appears at that depth.
If both are found during the same level, then they have the same depth. They are cousins exactly when their recorded parents are different, so returning that comparison is correct.
If only one target is found during a level, then the other target cannot be at that same depth. Since BFS will only move deeper afterward, the two nodes have different depths. They cannot be cousins, so returning False is correct.
Because both target values exist and BFS eventually visits every node, the algorithm correctly determines whether the two target nodes are cousins.
Complexity
Let n be the number of nodes and w be the maximum width of the tree.
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Each node is processed at most once |
| Space | O(w) | The queue stores one level of nodes |
In the worst case, w can be O(n).
Implementation
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: Optional[TreeNode], x: int, y: int) -> bool:
queue = deque([(root, None)])
while queue:
level_size = len(queue)
parent_x = None
parent_y = None
for _ in range(level_size):
node, parent = queue.popleft()
if node.val == x:
parent_x = parent
if node.val == y:
parent_y = parent
if node.left:
queue.append((node.left, node))
if node.right:
queue.append((node.right, node))
if parent_x is not None and parent_y is not None:
return parent_x != parent_y
if parent_x is not None or parent_y is not None:
return False
return FalseCode Explanation
The queue stores each node together with its parent:
queue = deque([(root, None)])For each loop iteration, we process one full depth level:
level_size = len(queue)At the start of each level, we have not found either target:
parent_x = None
parent_y = NoneWhen a target appears, store its parent:
if node.val == x:
parent_x = parent
if node.val == y:
parent_y = parentChildren are pushed with the current node as their parent:
if node.left:
queue.append((node.left, node))
if node.right:
queue.append((node.right, node))After finishing the level, if both targets were found, they have the same depth:
if parent_x is not None and parent_y is not None:
return parent_x != parent_yIf only one target was found, they are at different depths:
if parent_x is not None or parent_y is not None:
return FalseTesting
from collections import deque
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def run_tests():
s = Solution()
root1 = TreeNode(
1,
TreeNode(2, TreeNode(4)),
TreeNode(3),
)
assert s.isCousins(root1, 4, 3) is False
root2 = TreeNode(
1,
TreeNode(2, None, TreeNode(4)),
TreeNode(3, None, TreeNode(5)),
)
assert s.isCousins(root2, 5, 4) is True
root3 = TreeNode(
1,
TreeNode(2, None, TreeNode(4)),
TreeNode(3),
)
assert s.isCousins(root3, 2, 3) is False
root4 = TreeNode(
1,
TreeNode(2, TreeNode(4), TreeNode(5)),
TreeNode(3),
)
assert s.isCousins(root4, 4, 5) is False
print("all tests passed")
run_tests()| Test | Expected | Why |
|---|---|---|
x = 4, y = 3 | False | Different depths |
x = 5, y = 4 | True | Same depth, different parents |
x = 2, y = 3 | False | Same parent |
x = 4, y = 5 | False | Siblings, not cousins |