🧠
Better (Single DFS to find both nodes simultaneously)
💡 This approach improves efficiency by finding depth and parent of both nodes in a single DFS traversal, reducing redundant work and making the code cleaner.
Intuition
Instead of two separate traversals, we track both nodes' depth and parent in one DFS pass, then compare results.
Algorithm
- Initialize variables to store depth and parent for x and y.
- Perform a single DFS traversal, updating these variables when x or y is found.
- After traversal, compare the depths and parents of x and y.
- Return true if depths are equal and parents differ; otherwise, false.
💡 The key is to track both nodes in one traversal, which requires careful state management.
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def __init__(self):
self.x_depth = -1
self.y_depth = -1
self.x_parent = None
self.y_parent = None
def dfs(self, node: Optional[TreeNode], parent: Optional[TreeNode], depth: int, x: int, y: int):
if not node:
return
if node.val == x:
self.x_depth = depth
self.x_parent = parent
elif node.val == y:
self.y_depth = depth
self.y_parent = parent
self.dfs(node.left, node, depth + 1, x, y)
self.dfs(node.right, node, depth + 1, x, y)
def isCousins(self, root: Optional[TreeNode], x: int, y: int) -> bool:
self.dfs(root, None, 0, x, y)
return self.x_depth == self.y_depth and self.x_parent != self.y_parent
# Example usage:
# root = TreeNode(1, TreeNode(2, TreeNode(4)), TreeNode(3))
# sol = Solution()
# print(sol.isCousins(root, 4, 3)) # Output: False
Line Notes
class Solution:Encapsulate state to track x and y info during DFS
def dfs(self, node: Optional[TreeNode], parent: Optional[TreeNode], depth: int, x: int, y: int):Single DFS to find both nodes simultaneously
if node.val == x:Record depth and parent for x when found
elif node.val == y:Record depth and parent for y when found
self.dfs(node.left, node, depth + 1, x, y)Recurse left subtree with updated depth and parent
self.dfs(node.right, node, depth + 1, x, y)Recurse right subtree similarly
return self.x_depth == self.y_depth and self.x_parent != self.y_parentCheck cousin condition after DFS
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
private int xDepth = -1, yDepth = -1;
private TreeNode xParent = null, yParent = null;
private void dfs(TreeNode node, TreeNode parent, int depth, int x, int y) {
if (node == null) return;
if (node.val == x) {
xDepth = depth;
xParent = parent;
} else if (node.val == y) {
yDepth = depth;
yParent = parent;
}
dfs(node.left, node, depth + 1, x, y);
dfs(node.right, node, depth + 1, x, y);
}
public boolean isCousins(TreeNode root, int x, int y) {
dfs(root, null, 0, x, y);
return xDepth == yDepth && xParent != yParent;
}
// Example usage:
// public static void main(String[] args) {
// TreeNode root = new TreeNode(1);
// root.left = new TreeNode(2);
// root.right = new TreeNode(3);
// root.left.left = new TreeNode(4);
// Solution sol = new Solution();
// System.out.println(sol.isCousins(root, 4, 3)); // false
// }
}
Line Notes
private void dfs(TreeNode node, TreeNode parent, int depth, int x, int y) {Single DFS to find both nodes' depth and parent
if (node == null) return;Base case for recursion
if (node.val == x) {Record x's depth and parent
else if (node.val == y) {Record y's depth and parent
dfs(node.left, node, depth + 1, x, y);Recurse left subtree
dfs(node.right, node, depth + 1, x, y);Recurse right subtree
return xDepth == yDepth && xParent != yParent;Check cousin condition
#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int v) : val(v), left(nullptr), right(nullptr) {}
};
class Solution {
int xDepth = -1, yDepth = -1;
TreeNode* xParent = nullptr;
TreeNode* yParent = nullptr;
void dfs(TreeNode* node, TreeNode* parent, int depth, int x, int y) {
if (!node) return;
if (node->val == x) {
xDepth = depth;
xParent = parent;
} else if (node->val == y) {
yDepth = depth;
yParent = parent;
}
dfs(node->left, node, depth + 1, x, y);
dfs(node->right, node, depth + 1, x, y);
}
public:
bool isCousins(TreeNode* root, int x, int y) {
dfs(root, nullptr, 0, x, y);
return xDepth == yDepth && xParent != yParent;
}
};
// Example usage:
// int main() {
// TreeNode* root = new TreeNode(1);
// root->left = new TreeNode(2);
// root->right = new TreeNode(3);
// root->left->left = new TreeNode(4);
// Solution sol;
// cout << sol.isCousins(root, 4, 3) << endl; // Output: 0 (false)
// return 0;
// }
Line Notes
void dfs(TreeNode* node, TreeNode* parent, int depth, int x, int y) {Single DFS to find both nodes' info
if (!node) return;Base case for recursion
if (node->val == x) {Record x's depth and parent
else if (node->val == y) {Record y's depth and parent
dfs(node->left, node, depth + 1, x, y);Recurse left subtree
dfs(node->right, node, depth + 1, x, y);Recurse right subtree
return xDepth == yDepth && xParent != yParent;Check cousin condition
function TreeNode(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
function isCousins(root, x, y) {
let xDepth = -1, yDepth = -1;
let xParent = null, yParent = null;
function dfs(node, parent, depth) {
if (!node) return;
if (node.val === x) {
xDepth = depth;
xParent = parent;
} else if (node.val === y) {
yDepth = depth;
yParent = parent;
}
dfs(node.left, node, depth + 1);
dfs(node.right, node, depth + 1);
}
dfs(root, null, 0);
return xDepth === yDepth && xParent !== yParent;
}
// Example usage:
// const root = new TreeNode(1, new TreeNode(2, new TreeNode(4)), new TreeNode(3));
// console.log(isCousins(root, 4, 3)); // false
Line Notes
function dfs(node, parent, depth) {Single DFS to find both nodes' depth and parent
if (!node) return;Base case for recursion
if (node.val === x) {Record x's depth and parent
else if (node.val === y) {Record y's depth and parent
dfs(node.left, node, depth + 1);Recurse left subtree
dfs(node.right, node, depth + 1);Recurse right subtree
return xDepth === yDepth && xParent !== yParent;Check cousin condition
Single DFS traversal visits each node once, O(n). Recursion stack uses O(h) space.
💡 For n=100, this halves the work compared to brute force, making it more efficient and elegant.
Interview Verdict: Accepted
This is a clean and efficient solution suitable for interviews.