3203. Find Minimum Diameter After Merging Two Trees

Dare2Solve

Dare2Solve

3203. Find Minimum Diameter After Merging Two Trees
SAMSUNG 49-Inch Odyssey G9
SAMSUNG 49-Inch Odyssey G9
Because earth is not flat

Intuition

The problem requires connecting two trees such that the resulting tree has the minimum possible diameter. The diameter of a tree is defined as the longest path between any two nodes in the tree. To minimize the diameter after connecting two trees, we need to strategically choose nodes from each tree to connect.

Approach

  1. Calculate Tree Diameters: First, compute the diameters of both trees:

    • Use Depth-First Search (DFS) to find the diameter of each tree. Start from an arbitrary node (often node 0 for simplicity).
  2. Connecting Trees: After obtaining the diameters (d1 for the first tree and d2 for the second tree), the possible diameters of the resulting tree are:

    • d1 + d2 + 1: This accounts for the worst-case scenario where the longest path in the resulting tree goes through the connecting edge.
  3. Choose Connecting Nodes: To achieve the minimum diameter, connect nodes that minimize the impact on the existing diameters. A strategic choice is often connecting the centroid or a node close to the centroid of each tree.

  4. Compute Minimum Diameter: Calculate the possible diameters resulting from connecting different pairs of nodes from both trees and choose the minimum diameter.

  5. Implementation Details: Implement DFS to calculate tree diameters and carefully choose nodes for connection to minimize the resulting diameter. Edge cases include trees with minimal nodes (like single-node trees).

Complexity

This approach ensures that we find the optimal way to connect the two trees while minimizing the diameter of the resulting tree efficiently.

Code Explanation

var minimumDiameterAfterMerge = function (edges1, edges2) {
    var getDiameter = (edges) => {
let map = Array(edges.length + 1)
  .fill()
  .map(() => []);
for (let [i, j] of edges) {
  map[i].push(j);
  map[j].push(i);
}
let res = 0;
        var dfs = (node, parent) => {
            let maxDepth = 1;
            for (let neighbor of map[node]) {
                if (neighbor === parent) continue;
                let depth = dfs(neighbor, node);
                res = Math.max(res, maxDepth + depth);
                maxDepth = Math.max(maxDepth, 1 + depth);
            }
            return maxDepth;
        };
dfs(0, -1);
        return res;
    };
let d1 = getDiameter(edges1);
let d2 = getDiameter(edges2);
    return Math.max(d1 - 1, d2 - 1, Math.floor(d1 / 2) + Math.floor(d2 / 2) + 1);
};

Code

C++

class Solution {
public:
    int minimumDiameterAfterMerge(vector<vector<int>>& edges1, vector<vector<int>>& edges2) {
        int d1 = getDiameter(edges1);
        int d2 = getDiameter(edges2);
        return max(max(d1 - 1, d2 - 1), d1 / 2 + d2 / 2 + 1);
    }

private:
    int getDiameter(vector<vector<int>>& edges) {
        unordered_map<int, vector<int>> map;
        for (auto& edge : edges) {
            int i = edge[0];
            int j = edge[1];
            map[i].push_back(j);
            map[j].push_back(i);
        }

        int res = 0;
        dfs(0, -1, map, res);

        return res;
    }

    int dfs(int node, int parent, unordered_map<int, vector<int>>& map, int& res) {
        int maxDepth = 1;
        for (int neighbor : map[node]) {
            if (neighbor == parent)
                continue;
            int depth = dfs(neighbor, node, map, res);
            res = max(res, maxDepth + depth);
            maxDepth = max(maxDepth, 1 + depth);
        }
        return maxDepth;
    }
};

Python

class Solution:
    def minimumDiameterAfterMerge(self, edges1: List[List[int]], edges2: List[List[int]]) -> int:
        def getDiameter(edges):
            map = defaultdict(list)
            for i, j in edges:
                map[i].append(j)
                map[j].append(i)
            
            res = [0]
            
            def dfs(node, parent):
                maxDepth = 1
                for neighbor in map[node]:
                    if neighbor == parent:
                        continue
                    depth = dfs(neighbor, node)
                    res[0] = max(res[0], maxDepth + depth)
                    maxDepth = max(maxDepth, 1 + depth)
                return maxDepth
            
            dfs(0, -1)
            
            return res[0]
        
        d1 = getDiameter(edges1)
        d2 = getDiameter(edges2)
        
        return max(d1 - 1, d2 - 1, d1 // 2 + d2 // 2 + 1)

Java

class Solution {
    public int minimumDiameterAfterMerge(int[][] edges1, int[][] edges2) {
        int d1 = getDiameter(edges1);
        int d2 = getDiameter(edges2);
        return Math.max(Math.max(d1 - 1, d2 - 1), d1 / 2 + d2 / 2 + 1);
    }

    private int getDiameter(int[][] edges) {
        List<List<Integer>> map = new ArrayList<>();
        for (int i = 0; i < edges.length + 1; i++) {
            map.add(new ArrayList<>());
        }

        for (int[] edge : edges) {
            int i = edge[0];
            int j = edge[1];
            map.get(i).add(j);
            map.get(j).add(i);
        }

        int[] res = { 0 };

        dfs(0, -1, map, res);

        return res[0];
    }

    private int dfs(int node, int parent, List<List<Integer>> map, int[] res) {
        int maxDepth = 1;
        for (int neighbor : map.get(node)) {
            if (neighbor == parent)
                continue;
            int depth = dfs(neighbor, node, map, res);
            res[0] = Math.max(res[0], maxDepth + depth);
            maxDepth = Math.max(maxDepth, 1 + depth);
        }
        return maxDepth;
    }
}

JavaScript

/**
 * @param {number[][]} edges1
 * @param {number[][]} edges2
 * @return {number}
 */
var minimumDiameterAfterMerge = function (edges1, edges2) {
    var getDiameter = (edges) => {
        let map = Array(edges.length + 1).fill().map(() => []);

        for (let [i, j] of edges) {
            map[i].push(j);
            map[j].push(i);
        }

        let res = 0;

        var dfs = (node, parent) => {
            let maxDepth = 1;
            for (let neighbor of map[node]) {
                if (neighbor === parent) continue;
                let depth = dfs(neighbor, node);
                res = Math.max(res, maxDepth + depth);
                maxDepth = Math.max(maxDepth, 1 + depth);
            }
            return maxDepth;
        };

        dfs(0, -1);

        return res;
    };

    let d1 = getDiameter(edges1);
    let d2 = getDiameter(edges2);

    return Math.max(d1 - 1, d2 - 1, Math.floor(d1 / 2) + Math.floor(d2 / 2) + 1);
};