Forgetting null/base-case handling
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.
Move from brute-force thinking to an efficient approach using tree strategy.
You have n binary tree nodes numbered from 0 to n - 1 where node i has two children leftChild[i] and rightChild[i], return true if and only if all the given nodes form exactly one valid binary tree.
If node i has no left child then leftChild[i] will equal -1, similarly for the right child.
Note that the nodes have no values and that we only use the node numbers in this problem.
Example 1:
Input: n = 4, leftChild = [1,-1,3,-1], rightChild = [2,-1,-1,-1] Output: true
Example 2:
Input: n = 4, leftChild = [1,-1,3,-1], rightChild = [2,3,-1,-1] Output: false
Example 3:
Input: n = 2, leftChild = [1,0], rightChild = [-1,-1] Output: false
Constraints:
n == leftChild.length == rightChild.length1 <= n <= 104-1 <= leftChild[i], rightChild[i] <= n - 1Problem summary: You have n binary tree nodes numbered from 0 to n - 1 where node i has two children leftChild[i] and rightChild[i], return true if and only if all the given nodes form exactly one valid binary tree. If node i has no left child then leftChild[i] will equal -1, similarly for the right child. Note that the nodes have no values and that we only use the node numbers in this problem.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Tree · Union-Find
4 [1,-1,3,-1] [2,-1,-1,-1]
4 [1,-1,3,-1] [2,3,-1,-1]
2 [1,0] [-1,-1]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1361: Validate Binary Tree Nodes
class Solution {
private int[] p;
public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
p = new int[n];
for (int i = 0; i < n; ++i) {
p[i] = i;
}
boolean[] vis = new boolean[n];
for (int i = 0, m = n; i < m; ++i) {
for (int j : new int[] {leftChild[i], rightChild[i]}) {
if (j != -1) {
if (vis[j] || find(i) == find(j)) {
return false;
}
p[find(i)] = find(j);
vis[j] = true;
--n;
}
}
}
return n == 1;
}
private int find(int x) {
if (p[x] != x) {
p[x] = find(p[x]);
}
return p[x];
}
}
// Accepted solution for LeetCode #1361: Validate Binary Tree Nodes
func validateBinaryTreeNodes(n int, leftChild []int, rightChild []int) bool {
p := make([]int, n)
for i := range p {
p[i] = i
}
var find func(int) int
find = func(x int) int {
if p[x] != x {
p[x] = find(p[x])
}
return p[x]
}
vis := make([]bool, n)
for i, a := range leftChild {
for _, j := range []int{a, rightChild[i]} {
if j != -1 {
if vis[j] || find(i) == find(j) {
return false
}
p[find(i)] = find(j)
vis[j] = true
n--
}
}
}
return n == 1
}
# Accepted solution for LeetCode #1361: Validate Binary Tree Nodes
class Solution:
def validateBinaryTreeNodes(
self, n: int, leftChild: List[int], rightChild: List[int]
) -> bool:
def find(x: int) -> int:
if p[x] != x:
p[x] = find(p[x])
return p[x]
p = list(range(n))
vis = [False] * n
for i, (a, b) in enumerate(zip(leftChild, rightChild)):
for j in (a, b):
if j != -1:
if vis[j] or find(i) == find(j):
return False
p[find(i)] = find(j)
vis[j] = True
n -= 1
return n == 1
// Accepted solution for LeetCode #1361: Validate Binary Tree Nodes
struct Solution;
impl Solution {
fn validate_binary_tree_nodes(n: i32, left_child: Vec<i32>, right_child: Vec<i32>) -> bool {
let n = n as usize;
let mut indegree = vec![0; n];
let mut outdegree = vec![0; n];
let mut edge = 0;
for i in 0..n {
if left_child[i] != -1 {
edge += 1;
outdegree[i] += 1;
indegree[left_child[i] as usize] += 1;
}
if right_child[i] != -1 {
edge += 1;
outdegree[i] += 1;
indegree[right_child[i] as usize] += 1;
}
}
let degree = (0..n).any(|i| {
let a = n != 1 && indegree[i] == 0 && outdegree[i] == 0;
let b = indegree[i] > 1;
let c = outdegree[i] > 2;
a || b || c
});
!degree && edge + 1 == n
}
}
#[test]
fn test() {
let n = 4;
let left_child = vec![1, -1, 3, -1];
let right_child = vec![2, -1, -1, -1];
let res = true;
assert_eq!(
Solution::validate_binary_tree_nodes(n, left_child, right_child),
res
);
let n = 4;
let left_child = vec![1, -1, 3, -1];
let right_child = vec![2, 3, -1, -1];
let res = false;
assert_eq!(
Solution::validate_binary_tree_nodes(n, left_child, right_child),
res
);
let n = 2;
let left_child = vec![1, 0];
let right_child = vec![-1, -1];
let res = false;
assert_eq!(
Solution::validate_binary_tree_nodes(n, left_child, right_child),
res
);
let n = 6;
let left_child = vec![1, -1, -1, 4, -1, -1];
let right_child = vec![2, -1, -1, 5, -1, -1];
let res = false;
assert_eq!(
Solution::validate_binary_tree_nodes(n, left_child, right_child),
res
);
}
// Accepted solution for LeetCode #1361: Validate Binary Tree Nodes
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #1361: Validate Binary Tree Nodes
// class Solution {
// private int[] p;
//
// public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
// p = new int[n];
// for (int i = 0; i < n; ++i) {
// p[i] = i;
// }
// boolean[] vis = new boolean[n];
// for (int i = 0, m = n; i < m; ++i) {
// for (int j : new int[] {leftChild[i], rightChild[i]}) {
// if (j != -1) {
// if (vis[j] || find(i) == find(j)) {
// return false;
// }
// p[find(i)] = find(j);
// vis[j] = true;
// --n;
// }
// }
// }
// return n == 1;
// }
//
// private int find(int x) {
// if (p[x] != x) {
// p[x] = find(p[x]);
// }
// return p[x];
// }
// }
Use this to step through a reusable interview workflow for this problem.
BFS with a queue visits every node exactly once — O(n) time. The queue may hold an entire level of the tree, which for a complete binary tree is up to n/2 nodes = O(n) space. This is optimal in time but costly in space for wide trees.
Every node is visited exactly once, giving O(n) time. Space depends on tree shape: O(h) for recursive DFS (stack depth = height h), or O(w) for BFS (queue width = widest level). For balanced trees h = log n; for skewed trees h = n.
Review these before coding to avoid predictable interview regressions.
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.