Problem Walkthrough

Minimum Height Trees LeetCode 310 Guide

Find the tree roots that minimize height by iteratively removing leaf nodes — degree-1 nodes — layer by layer until only 1 or 2 center nodes remain. These surviving nodes are the only possible MHT roots.

9 min read|

Minimum Height Trees

LeetCode 310

Problem Overview — Find All Roots That Minimize Tree Height

LeetCode 310 Minimum Height Trees gives you an undirected tree with n nodes labeled 0 to n-1 and n-1 edges. Your goal is to find all nodes that, when chosen as root, result in a tree of minimum possible height. Multiple valid roots may exist, and you must return all of them as a list in any order.

The height of a rooted tree is the number of edges on the longest path from root to any leaf. If you root the same tree at different nodes, the height changes. Nodes near the center of the tree tend to produce shorter trees because they are closer to all other nodes. Nodes near the periphery — the leaves — produce the tallest trees.

The key constraint is that the input is guaranteed to be a tree: it is connected, has no cycles, and has exactly n-1 edges. This makes certain properties reliable — notably that the graph has a well-defined center and diameter, which the solution exploits.

  • n nodes labeled 0 to n-1, 1 <= n <= 2 * 10^4
  • Exactly n-1 edges: the input is always a valid tree (connected, acyclic)
  • Find all roots that minimize the height of the resulting rooted tree
  • Return the list of root labels — order does not matter
  • At most 2 valid MHT roots exist for any tree

Brute Force and Why It Is Slow

The naive approach tries every node as a root: run BFS or DFS from that node to find the maximum depth, then track the minimum height found across all n starting nodes. This is correct but runs in O(n^2) time — for each of the n nodes you do an O(n) BFS traversal. On a tree with n = 20,000 nodes this is 400 million operations, which is too slow.

The brute force also does a lot of redundant work. When you re-root the tree from node A to its neighbor B, most of the subtree structure stays the same — only the perspective shifts. Re-rooting techniques can exploit this to update the height in O(1) per node, but they are complex to implement correctly under interview pressure.

What we need is a single-pass O(n) algorithm that finds the center(s) directly without trying each root. The leaf trimming approach provides exactly that, using the structural properties of trees.

💡

At Most 2 MHT Roots — Always

The MHT roots are always the center node(s) of the tree's longest path (diameter). A path has either one midpoint (odd length) or two adjacent midpoints (even length). This means there are always at most 2 valid MHT roots — never 3 or more. This is a key insight to share in interviews.

Leaf Trimming Intuition — Peel the Onion Inward

A leaf node has degree 1 — it connects to exactly one other node. If you root the tree at a leaf, the height is at least n-1 in a path graph, and generally much taller than rooting at a central node. Therefore, leaves can never be the optimal root unless n=1 or n=2. We can safely eliminate all current leaves from consideration.

After removing the first layer of leaves, the remaining nodes form a smaller tree (or forest that quickly reconnects). Some of the remaining nodes now have degree 1 in the reduced graph — they are the new leaves. These new leaves also cannot be optimal roots because the nodes deeper inside are still closer to the overall center. We peel again.

This process of peeling layer by layer from the outside inward is like peeling an onion. Each round of peeling removes the outermost ring of leaves. Eventually, you are left with either 1 node (odd-diameter tree) or 2 adjacent nodes (even-diameter tree). These surviving nodes are exactly the MHT roots.

  1. 1Build adjacency list and degree (neighbor count) array for all nodes
  2. 2Identify all initial leaves: nodes with degree == 1
  3. 3Remove the leaf layer: subtract 1 from each leaf neighbor's degree
  4. 4Any neighbor whose degree drops to 1 becomes the next round's leaf
  5. 5Repeat until only 1 or 2 nodes remain in the "active" pool
  6. 6The remaining nodes are the MHT roots — return them

BFS Leaf Removal — Algorithm Details

Initialize a queue with all degree-1 nodes (leaves). Keep a count of remaining nodes, starting at n. Each BFS round, process the entire current queue (one full layer of leaves): for each leaf, remove it from consideration by decrementing the remaining count. For each neighbor of the removed leaf, decrement that neighbor's degree. If the neighbor's degree drops to 1, it is the next round's leaf — add it to the next queue.

The loop continues while remaining nodes > 2. Once only 1 or 2 nodes remain, those are the answer. You never need to do a full BFS from those roots — the trimming process guarantees they are the centers. The trick is processing an entire queue layer at once (like BFS level order) so that all leaves at the same depth are removed simultaneously.

Using sets for the adjacency list (instead of lists) makes neighbor removal trivially O(1). With adjacency sets, when you remove a leaf you can also remove it from its neighbor's set, ensuring degree tracking stays accurate. This is not strictly necessary — the degree array alone is sufficient — but it is a clean implementation choice.

ℹ️

This Is Reverse Topological Sort

Standard topological sort removes sources — nodes with in-degree 0. Leaf trimming removes sinks in an undirected sense — nodes with degree 1. Both algorithms peel from the boundary inward. The leaf pruning approach for MHT is conceptually the same as topological sort run in reverse on the undirected tree, narrowing toward the center instead of ordering from the start.

Why There Are Always 1 or 2 Centers

The centers of a tree are defined as the midpoint(s) of its diameter — the longest path between any two nodes. If the diameter has odd length, there is exactly one midpoint node. If the diameter has even length, there are exactly two adjacent midpoint nodes. No tree can have a diameter with more than one midpoint or two adjacent midpoints — this is a fundamental property of paths.

The leaf trimming algorithm converges to these center nodes because it peels from both ends of the diameter simultaneously, closing in on the midpoint(s). Peeling from one side of the diameter advances one step per round, and since both ends shrink symmetrically, the algorithm halts exactly when the center is exposed.

This also explains why the stopping condition is remaining <= 2 rather than remaining == 1. If the tree's diameter is even, the two center nodes are the last two nodes standing. If we waited for just one, we would incorrectly eliminate one of the two valid MHT roots. Always stop at 2.

  1. 1Tree diameter = length of longest path between any two nodes
  2. 2Odd diameter length → one center node at the exact midpoint
  3. 3Even diameter length → two adjacent center nodes (both midpoints)
  4. 4Leaf trimming peels both ends of the diameter simultaneously each round
  5. 5Algorithm halts when 1 or 2 nodes remain — these are the centers
  6. 6Never stop at remaining == 1; stop at remaining <= 2 to capture both cases

Code Walkthrough — Python and Java

In Python, build an adjacency list using defaultdict(set) and compute a degree array in a single loop over edges. Initialize a deque with all nodes where degree[i] == 1 (handle n==1 edge case: return [0] immediately). Set remaining = n. While remaining > 2: process all current leaves — for each leaf, remaining -= 1, for each neighbor remove the leaf from its adjacency set and decrement degree; if degree drops to 1, add to next_leaves queue. After the loop, return list of remaining active nodes.

In Java, use ArrayList<Set<Integer>> for the adjacency list and an int[] degree array. Use ArrayDeque<Integer> for the BFS queue. For each edge, add both directions to the adjacency sets and increment both degrees. Seed the queue with all degree-1 nodes. While remaining > 2: process current queue size in a for loop (not while), decrement remaining per leaf, update neighbor degrees, enqueue new leaves. Collect remaining nodes: iterate all n nodes, return those with degree >= 1 or those still in adjacency sets.

Both implementations run in O(n) time and O(n) space. The time bound comes from the fact that each node is added to the leaf queue exactly once and each edge is visited at most twice. Space is O(n) for the adjacency list and degree array. This is optimal — you must read all n nodes and n-1 edges at minimum.

⚠️

Handle n=1 and n=2 Edge Cases

For n=1 there are no edges and no leaves in the traditional sense — return [0] immediately before any BFS logic. For n=2 the loop condition remaining > 2 is false from the start, so both nodes are returned correctly without entering the loop. Test these edge cases explicitly: they are common in interviews and easy to break with off-by-one errors in the stopping condition.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now