Minimum Height Trees

Updated on 18 June, 2025
Minimum Height Trees header image

Problem Statement

In the context of graph theory, a tree is a special form of an undirected graph. Each pair of nodes is connected by exactly one path, indicating that there are no cycles, and every node can be reached from any other node. Given a set of n nodes labeled from 0 to n - 1, together with n - 1 edges connecting specific pairs of these nodes, this setup forms such a tree structure. Each edges[i] contains a pair [ai, bi], symbolizing an undirected connection between nodes ai and bi.

In this scenario, by designating any node as the root, a new rooted tree formulates with a specific height h, defined as the maximum number of edges in any path from the root down to a leaf. Our objective is to identify all nodes that can serve as the root such that the resulting tree has the minimal possible height. These optimal roots hence define the Minimum Height Trees (MHTs) for the given tree structure. The task is to return the labels of these MHT roots in any order.

Examples

Example 1

Input:

n = 4, edges = [[1,0],[1,2],[1,3]]

Output:

[1]

Explanation:

As shown, the height of the tree is 1 when the root is the node with label 1 which is the only MHT.

Example 2

Input:

n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]

Output:

[3,4]

Constraints

  • 1 <= n <= 2 * 104
  • edges.length == n - 1
  • 0 <= ai, bi < n
  • ai != bi
  • All the pairs (ai, bi) are distinct.
  • The given input is guaranteed to be a tree and there will be no repeated edges.

Approach and Intuition

Understanding how to determine the Minimum Height Trees (MHTs) involves a few key concepts and steps:

  1. Understanding Tree Centrality:

    • The key to solving this problem is identifying the "center" of the tree. Popularly, it is established that for a tree, either one or two nodes form the tree's center. These centers, when chosen as roots, result in the minimum height of the tree.
  2. Identifying the MHTs involves reducing the tree:

    • Initially, consider nodes with only one connection (leaves). These leaves are peripheral and thus cannot be centers since they increase tree height.
    • Remove the leaves from consideration and define the new leaves in the scaled-down tree. Continue this reduction until one or two nodes remain, which are the central nodes.
  3. Applying the Reduction Algorithm:

    • Begin by identifying all leaves (nodes with a single connection).
    • Remove these leaves and decrease the degree of their connected nodes.
    • Repeat the process: identify new leaves, remove, and update connections.
    • The process ends when only one or two nodes are left, which are our candidates for MHTs.
  4. Efficiency Insight:

    • This peeling process essentially performs a breadth-first search (BFS) but in reverse, starting from the outermost nodes and moving inward. This is efficient, as each edge and node is effectively processed once.

Implementing these steps ensures that the central nodes are identified which, when used as roots, give the minimal height trees. This method is efficient, intuitive, and lends itself well to the given constraints. The examples provided help illustrate this method where peeling off layers of leaves step-by-step reveals the central node(s) that minimize the tree's height.

Solutions

  • Java
  • Python
java
class Solution {
    public List<Integer> computeMinimumHeightTrees(int totalNodes, int[][] connections) {

        if (totalNodes < 2) {
            ArrayList<Integer> centroids = new ArrayList<>();
            for (int node = 0; node < totalNodes; node++)
                centroids.add(node);
            return centroids;
        }

        ArrayList<Set<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < totalNodes; i++)
            graph.add(new HashSet<Integer>());

        for (int[] link : connections) {
            Integer point1 = link[0], point2 = link[1];
            graph.get(point1).add(point2);
            graph.get(point2).add(point1);
        }

        ArrayList<Integer> firstLayerLeaves = new ArrayList<>();
        for (int i = 0; i < totalNodes; i++)
            if (graph.get(i).size() == 1)
                firstLayerLeaves.add(i);

        int remainNodes = totalNodes;
        while (remainNodes > 2) {
            remainNodes -= firstLayerLeaves.size();
            ArrayList<Integer> newLayerLeaves = new ArrayList<>();

            for (Integer leaf : firstLayerLeaves) {
                Integer singleNeighbor = graph.get(leaf).iterator().next();
                graph.get(singleNeighbor).remove(leaf);
                if (graph.get(singleNeighbor).size() == 1)
                    newLayerLeaves.add(singleNeighbor);
            }

            firstLayerLeaves = newLayerLeaves;
        }

        return firstLayerLeaves;
    }
}

The code provided solves the problem of finding the minimum height trees in a graph, specifically represented as an undirected and connected tree graph with totalNodes nodes and connections as node pairs. Implement this solution in Java to efficiently pinpoint the central nodes from which the tree heights are minimal.

  • Start by addressing cases with fewer than two nodes. In such scenarios, all concerned nodes are potential answers since either the graph is empty or has just one node, both resulting in a tree height of zero.
  • Create an adjacency list graph to effectively manage and access the tree structure. Each node maps to a set of connected nodes, facilitating easy modifications and checks.
  • Identify all leaves, which are nodes with only one connection. This forms the first layer of leaf nodes.
  • Gradually trim the leaves layer by layer, ensuring to decrease the total count of nodes remaining in the consideration by the size of the current layer of leaves.
  • Once the nodes remaining are two or less, you have arrived at the potential minimum height trees' roots. These nodes are then returned.

Through this iterative reduction of leaves, the process converges at the minimal height trees' roots by effectively narrowing down to the center of the graph regardless of its initial configuration. This code is structured to handle various sizes and complexities of tree structures, ensuring both performance and correctness in identifying centroid nodes from where the tree height is minimized.

python
class Solution:
    def minHeightTrees(self, node_count: int, connections: List[List[int]]) -> List[int]:
        # Handling special cases
        if node_count <= 2:
            return [i for i in range(node_count)]

        # Constructing graph using adjacency set
        graph = [set() for _ in range(node_count)]
        for src, dest in connections:
            graph[src].add(dest)
            graph[dest].add(src)

        # Starting with initial leaf nodes
        initial_leaves = [i for i in range(node_count) if len(graph[i]) == 1]

        # Pruning the leaves until we are left with the centroids
        active_nodes = node_count
        while active_nodes > 2:
            active_nodes -= len(initial_leaves)
            temp_leaves = []

            # Removing leaves
            while initial_leaves:
                leaf = initial_leaves.pop()
                # There’s exactly one neighbor
                adj_node = graph[leaf].pop()
                # Detach the leaf from its neighbor
                graph[adj_node].remove(leaf)
                if len(graph[adj_node]) == 1:
                    temp_leaves.append(adj_node)

            # Updating leaves
            initial_leaves = temp_leaves

        # Answer is the last remaining leaves
        return initial_leaves

The stated Python solution focuses on finding the Minimum Height Trees (MHTs) from a given set of nodes and their connections. Generally used for large graphs, this algorithm effectively tracks down the central nodes that, when chosen as tree roots, ensure trees have minimal heights.

Here's a breakdown of how the implementation works:

  • Start by evaluating if the provided node_count is two or less. For such minimal cases, simply return a list containing all nodes.
  • Construct an adjacency set to represent the graph. This way, connect every pair of nodes defined in connections.
  • Identify leaf nodes, which are those with only one connection. These nodes become the starting point for pruning the tree.
  • Continuously prune these leaves until there are no more than two central nodes left:
    1. Track the number of active nodes, and adjust this count as leaf nodes are pruned.
    2. For each remaining leaf, disconnect it from its only adjacent node. If this action turns the adjacent node into a leaf, prepare to prune it in the next iteration.
    3. Update the list of leaf nodes to only include newly formed leaf nodes from the recent pruning step.
  • The remaining nodes post-pruning represent the roots of the trees with the minimum height; hence they are returned.

By efficiently narrowing down the potential root nodes, this method ensures a performance-friendly approach to solving an otherwise complex problem. Consider it a blend of mathematical and algorithmic strategies to achieve an optimal solution. Also, the discussed procedure revolves around core operations on basic data structures, such as sets and lists, making the provided solution both elegant and effective.

Comments

No comments yet.