1. Problem Summary
You are given an undirected graph representing a tree with:
nnodes labeled from0ton-1edges[i] = [a, b]representing a bidirectional connection
Your task is to return all node labels that can serve as roots of Minimum Height Trees (MHTs).
Important characteristics:
- A tree has no cycles and is connected
- Height is defined as the longest distance from root to any leaf
- There may be one or two valid MHT roots
- If
n == 1, the only root is[0]
Example interpretation:
For input:
n = 4 edges = [[1,0],[1,2],[1,3]]
The tree rooted at node 1 has minimum height, so output is:
[1]
2. Key Insights
A Minimum Height Tree root lies at the center of the tree
Trees have centroid nodes located in the middle of longest paths.
A tree can have:
- 1 centroid (odd-length diameter)
- 2 centroids (even-length diameter)
Removing leaves layer by layer reveals the centroids
Leaves = nodes with degree 1
When stripped away iteratively:
- Tree shrinks inward
- The last remaining nodes are centroids (MHT roots)
BFS leaf trimming is more efficient than computing height from each node
Naive method → O(n²), too slow
Leaf trimming → O(n)
3. Approach
Topological leaf-trimming (BFS)
Steps:
- Handle special case:
if n == 1 → return [0] - Build adjacency list and degree count for each node.
- Initialize a queue with all initial leaves (degree == 1).
- Track remaining node count.
- Repeatedly trim leaves:
- Remove current leaves
- Decrease degree of neighbors
- Newly formed leaves enter queue
- Continue until:
remainingNodes <= 2 - Remaining nodes in queue are MHT roots.
4. Time and Space Complexity
Time Complexity: O(n)
Every edge and node processed a constant number of times.
Space Complexity: O(n)
Adjacency list + queue storage.
5. Java Solution
import java.util.*;
class Solution {
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
if (n == 1) {
return Collections.singletonList(0);
}
List<List<Integer>> adj = new ArrayList<>();
for (int i = 0; i < n; i++) {
adj.add(new ArrayList<>());
}
int[] degree = new int[n];
for (int[] edge : edges) {
int a = edge[0];
int b = edge[1];
adj.get(a).add(b);
adj.get(b).add(a);
degree[a]++;
degree[b]++;
}
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (degree[i] == 1) {
queue.offer(i);
}
}
int remaining = n;
while (remaining > 2) {
int size = queue.size();
remaining -= size;
for (int i = 0; i < size; i++) {
int leaf = queue.poll();
for (int neighbor : adj.get(leaf)) {
degree[neighbor]--;
if (degree[neighbor] == 1) {
queue.offer(neighbor);
}
}
}
}
return new ArrayList<>(queue);
}
}
6. Dry Run Examples
Example 1
Input:
n = 4 edges = [[1,0],[1,2],[1,3]]
Degrees:
0:1, 1:3, 2:1, 3:1
Initial leaves queue:
[0, 2, 3]
Remove leaves:
remaining = 4 – 3 = 1
Node 1 becomes degree 1 → remains
Remaining node:
[1]
Output:
[1]
Example 2
Input:
n = 6 edges = [[0,3],[1,3],[2,3],[4,3],[5,4]]
Graph centers on 3 and 4
Final result:
[3,4]
Example 3
Input:
n = 1 edges = []
Output:
[0]
Example 4
Input:
n = 2 edges = [[0,1]]
Both nodes valid
Output:
[0,1]
7. Why This Solution Works
- Uses proven centroid property of trees
- Avoids computing height repeatedly
- Shrinks graph layer by layer
- Remaining nodes always correspond to MHT roots
- Works for large n efficiently
8. Common Mistakes
- Trying BFS/DFS height calculation from every node (too slow)
- Forgetting special case when n = 1
- Mismanaging degree decrement
- Assuming only one root always exists
- Treating graph as directed instead of undirected
