Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You are given an array pairs, where pairs[i] = [xi, yi], and:
xi < yiLet ways be the number of rooted trees that satisfy the following conditions:
pairs.[xi, yi] exists in pairs if and only if xi is an ancestor of yi or yi is an ancestor of xi.Two ways are considered to be different if there is at least one node that has different parents in both ways.
Return:
0 if ways == 01 if ways == 12 if ways > 1A rooted tree is a tree that has a single root node, and all edges are oriented to be outgoing from the root.
An ancestor of a node is any node on the path from the root to that node (excluding the node itself). The root has no ancestors.
Example 1:
Input: pairs = [[1,2],[2,3]] Output: 1 Explanation: There is exactly one valid rooted tree, which is shown in the above figure.
Example 2:
Input: pairs = [[1,2],[2,3],[1,3]] Output: 2 Explanation: There are multiple valid rooted trees. Three of them are shown in the above figures.
Example 3:
Input: pairs = [[1,2],[2,3],[2,4],[1,5]] Output: 0 Explanation: There are no valid rooted trees.
Constraints:
1 <= pairs.length <= 1051 <= xi < yi <= 500pairs are unique.Problem summary: You are given an array pairs, where pairs[i] = [xi, yi], and: There are no duplicates. xi < yi Let ways be the number of rooted trees that satisfy the following conditions: The tree consists of nodes whose values appeared in pairs. A pair [xi, yi] exists in pairs if and only if xi is an ancestor of yi or yi is an ancestor of xi. Note: the tree does not have to be a binary tree. Two ways are considered to be different if there is at least one node that has different parents in both ways. Return: 0 if ways == 0 1 if ways == 1 2 if ways > 1 A rooted tree is a tree that has a single root node, and all edges are oriented to be outgoing from the root. An ancestor of a node is any node on the path from the root to that node (excluding the node itself). The root has no ancestors.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Tree
[[1,2],[2,3]]
[[1,2],[2,3],[1,3]]
[[1,2],[2,3],[2,4],[1,5]]
create-binary-tree-from-descriptions)maximum-star-sum-of-a-graph)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1719: Number Of Ways To Reconstruct A Tree
class Solution {
public int checkWays(int[][] pairs) {
boolean[][] g = new boolean[510][510];
List<Integer>[] v = new List[510];
Arrays.setAll(v, k -> new ArrayList<>());
for (int[] p : pairs) {
int x = p[0], y = p[1];
g[x][y] = true;
g[y][x] = true;
v[x].add(y);
v[y].add(x);
}
List<Integer> nodes = new ArrayList<>();
for (int i = 0; i < 510; ++i) {
if (!v[i].isEmpty()) {
nodes.add(i);
g[i][i] = true;
}
}
nodes.sort(Comparator.comparingInt(a -> v[a].size()));
boolean equal = false;
int root = 0;
for (int i = 0; i < nodes.size(); ++i) {
int x = nodes.get(i);
int j = i + 1;
for (; j < nodes.size() && !g[x][nodes.get(j)]; ++j)
;
if (j < nodes.size()) {
int y = nodes.get(j);
if (v[x].size() == v[y].size()) {
equal = true;
}
for (int z : v[x]) {
if (!g[y][z]) {
return 0;
}
}
} else {
++root;
}
}
if (root > 1) {
return 0;
}
return equal ? 2 : 1;
}
}
// Accepted solution for LeetCode #1719: Number Of Ways To Reconstruct A Tree
func checkWays(pairs [][]int) int {
g := make([][]bool, 510)
v := make([][]int, 510)
for i := range g {
g[i] = make([]bool, 510)
}
for _, p := range pairs {
x, y := p[0], p[1]
g[x][y] = true
g[y][x] = true
v[x] = append(v[x], y)
v[y] = append(v[y], x)
}
var nodes []int
for i := 1; i <= 500; i++ {
if len(v[i]) > 0 {
nodes = append(nodes, i)
g[i][i] = true
}
}
sort.Slice(nodes, func(i, j int) bool {
return len(v[nodes[i]]) < len(v[nodes[j]])
})
equal := false
root := 0
for i, x := range nodes {
j := i + 1
for ; j < len(nodes) && !g[x][nodes[j]]; j++ {
}
if j < len(nodes) {
y := nodes[j]
if len(v[x]) == len(v[y]) {
equal = true
}
for _, z := range v[x] {
if !g[y][z] {
return 0
}
}
} else {
root++
}
}
if root > 1 {
return 0
}
if equal {
return 2
}
return 1
}
# Accepted solution for LeetCode #1719: Number Of Ways To Reconstruct A Tree
class Solution:
def checkWays(self, pairs: List[List[int]]) -> int:
g = [[False] * 510 for _ in range(510)]
v = defaultdict(list)
for x, y in pairs:
g[x][y] = g[y][x] = True
v[x].append(y)
v[y].append(x)
nodes = []
for i in range(510):
if v[i]:
nodes.append(i)
g[i][i] = True
nodes.sort(key=lambda x: len(v[x]))
equal = False
root = 0
for i, x in enumerate(nodes):
j = i + 1
while j < len(nodes) and not g[x][nodes[j]]:
j += 1
if j < len(nodes):
y = nodes[j]
if len(v[x]) == len(v[y]):
equal = True
for z in v[x]:
if not g[y][z]:
return 0
else:
root += 1
if root > 1:
return 0
return 2 if equal else 1
// Accepted solution for LeetCode #1719: Number Of Ways To Reconstruct A Tree
/**
* [1719] Number Of Ways To Reconstruct A Tree
*
* You are given an array pairs, where pairs[i] = [xi, yi], and:
*
* There are no duplicates.
* xi < yi
*
* Let ways be the number of rooted trees that satisfy the following conditions:
*
* The tree consists of nodes whose values appeared in pairs.
* A pair [xi, yi] exists in pairs if and only if xi is an ancestor of yi or yi is an ancestor of xi.
* Note: the tree does not have to be a binary tree.
*
* Two ways are considered to be different if there is at least one node that has different parents in both ways.
* Return:
*
* 0 if ways == 0
* 1 if ways == 1
* 2 if ways > 1
*
* A rooted tree is a tree that has a single root node, and all edges are oriented to be outgoing from the root.
* An ancestor of a node is any node on the path from the root to that node (excluding the node itself). The root has no ancestors.
*
* Example 1:
* <img src="https://assets.leetcode.com/uploads/2020/12/03/trees2.png" style="width: 208px; height: 221px;" />
* Input: pairs = [[1,2],[2,3]]
* Output: 1
* Explanation: There is exactly one valid rooted tree, which is shown in the above figure.
*
* Example 2:
* <img alt="" src="https://assets.leetcode.com/uploads/2020/12/03/tree.png" style="width: 234px; height: 241px;" />
* Input: pairs = [[1,2],[2,3],[1,3]]
* Output: 2
* Explanation: There are multiple valid rooted trees. Three of them are shown in the above figures.
*
* Example 3:
*
* Input: pairs = [[1,2],[2,3],[2,4],[1,5]]
* Output: 0
* Explanation: There are no valid rooted trees.
*
* Constraints:
*
* 1 <= pairs.length <= 10^5
* 1 <= xi < yi <= 500
* The elements in pairs are unique.
*
*/
pub struct Solution {}
// problem: https://leetcode.com/problems/number-of-ways-to-reconstruct-a-tree/
// discuss: https://leetcode.com/problems/number-of-ways-to-reconstruct-a-tree/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
impl Solution {
// Credit: https://leetcode.com/problems/number-of-ways-to-reconstruct-a-tree/solutions/3200062/just-a-runnable-solution/
pub fn check_ways(pairs: Vec<Vec<i32>>) -> i32 {
let mut adj: std::collections::HashMap<i32, std::collections::HashSet<i32>> =
std::collections::HashMap::new();
for pair in pairs.iter() {
adj.entry(pair[0]).or_default().insert(pair[1]);
adj.entry(pair[1]).or_default().insert(pair[0]);
}
let mut q: std::collections::BinaryHeap<(usize, i32)> = std::collections::BinaryHeap::new();
for (x, arr) in adj.iter() {
q.push((arr.len(), *x));
}
let n = q.len();
let mut multiple = false;
let mut seen: std::collections::HashSet<i32> = std::collections::HashSet::new();
while let Some((sz, v)) = q.pop() {
let mut u = 0;
let mut usz = n + 1;
if !seen.is_empty() {
for x in adj.get(&v).unwrap() {
if adj.get(x).unwrap().len() < usz && seen.contains(x) {
u = *x;
usz = adj.get(x).unwrap().len();
}
}
}
seen.insert(v);
if u == 0 {
if sz != (n - 1) {
return 0;
}
continue;
}
for x in adj.get(&v).unwrap() {
if *x == u {
continue;
}
if !adj.get(&u).unwrap().contains(x) {
return 0;
}
}
if usz == sz {
multiple = true;
}
}
if multiple { 2 } else { 1 }
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_1719_example_1() {
let pairs = vec![vec![1, 2], vec![2, 3]];
let result = 1;
assert_eq!(Solution::check_ways(pairs), result);
}
#[test]
fn test_1719_example_2() {
let pairs = vec![vec![1, 2], vec![2, 3], vec![1, 3]];
let result = 2;
assert_eq!(Solution::check_ways(pairs), result);
}
#[test]
fn test_1719_example_3() {
let pairs = vec![vec![1, 2], vec![2, 3], vec![2, 4], vec![1, 5]];
let result = 0;
assert_eq!(Solution::check_ways(pairs), result);
}
}
// Accepted solution for LeetCode #1719: Number Of Ways To Reconstruct A Tree
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #1719: Number Of Ways To Reconstruct A Tree
// class Solution {
// public int checkWays(int[][] pairs) {
// boolean[][] g = new boolean[510][510];
// List<Integer>[] v = new List[510];
// Arrays.setAll(v, k -> new ArrayList<>());
// for (int[] p : pairs) {
// int x = p[0], y = p[1];
// g[x][y] = true;
// g[y][x] = true;
// v[x].add(y);
// v[y].add(x);
// }
// List<Integer> nodes = new ArrayList<>();
// for (int i = 0; i < 510; ++i) {
// if (!v[i].isEmpty()) {
// nodes.add(i);
// g[i][i] = true;
// }
// }
// nodes.sort(Comparator.comparingInt(a -> v[a].size()));
// boolean equal = false;
// int root = 0;
// for (int i = 0; i < nodes.size(); ++i) {
// int x = nodes.get(i);
// int j = i + 1;
// for (; j < nodes.size() && !g[x][nodes.get(j)]; ++j)
// ;
// if (j < nodes.size()) {
// int y = nodes.get(j);
// if (v[x].size() == v[y].size()) {
// equal = true;
// }
// for (int z : v[x]) {
// if (!g[y][z]) {
// return 0;
// }
// }
// } else {
// ++root;
// }
// }
// if (root > 1) {
// return 0;
// }
// return equal ? 2 : 1;
// }
// }
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: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
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.