LeetCode 310. Minimum Height Trees(最小高度树)

原题网址:https://leetcode.com/problems/minimum-height-trees/

For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels.

Format
The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels).

You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Example 1:

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

        0
        |
        1
       / \
      2   3

return [1]

Example 2:

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

     0  1  2
      \ | /
        3
        |
        4
        |
        5

return [3, 4]

Hint:

  1. How many MHTs can a graph have at most?

Note:

(1) According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.”

(2) The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.

方法一:最小高度树的根一定在图的一条最长路径的中间,寻找这条最长路径的方法是从任意一点出发,找到最远的点,然后再从这个最远的点出发,找到离它最远的点。可以用广度优先或者深度优先搜索。

public class Solution {
    private int maxNode, maxDepth;
    private void dfs(int from, int depth, List<Integer>[] graph, boolean[] visited, int[] dist, int[] prev) {
        if (depth > maxDepth) {
            maxDepth = depth;
            maxNode = from;
        }
        for(int next: graph[from]) {
            if (visited[next]) continue;
            visited[next] = true;
            prev[next] = from;
            dist[next] = depth+1;
            dfs(next, depth+1, graph, visited, dist, prev);
        }
    }
    private List<Integer> roots = new ArrayList<>();
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        List<Integer>[] graph = new ArrayList[n];
        for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
        for(int i=0; i<edges.length; i++) {
            graph[edges[i][0]].add(edges[i][1]);
            graph[edges[i][1]].add(edges[i][0]);
        }
        
        boolean[] visited = new boolean[n];
 
        int[] dist1 = new int[n];
        int[] prev1 = new int[n];
        maxNode = 0;
        maxDepth = 0;
        visited[0] = true;
        dfs(0, 0, graph, visited, dist1, prev1);
        int node1 = maxNode;

        int[] dist2 = new int[n];
        int[] prev2 = new int[n];
        Arrays.fill(visited, false);
        maxNode = node1;
        maxDepth = 0;
        visited[node1] = true;
        dfs(node1, 0, graph, visited, dist2, prev2);
        int node2 = maxNode;

        int node = node2;
        for(int i=0; i<maxDepth/2; i++) node = prev2[node];
        if ((maxDepth & 1) == 0) {
            roots.add(node);
        } else {
            roots.add(node);
            roots.add(prev2[node]);
        }
        return roots;
    }
}

优化,利用prev特性避免使用visit数组:

public class Solution {
    private void find(int curr, int dist, int[] prev, List<Integer>[] graph, Node max) {
        if (dist >= max.dist) {
            max.dist = dist;
            max.id = curr;
        }
        for(int i=0; i<graph[curr].size(); i++) {
            int next = graph[curr].get(i);
            if (next == prev[curr]) continue;
            prev[next] = curr;
            find(next, dist+1, prev, graph, max);
        }
    }
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        List<Integer>[] graph = new List[n];
        for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
        for(int[] edge: edges) {
            graph[edge[0]].add(edge[1]);
            graph[edge[1]].add(edge[0]);
        }
        int[] prev = new int[n];
        Arrays.fill(prev, -1);
        Node node1 = new Node();
        find(0, 0, prev, graph, node1);
        Arrays.fill(prev, -1);
        Node node2 = new Node();
        find(node1.id, 0, prev, graph, node2);
        List<Integer> roots = new ArrayList<>();
        int id = node2.id;
        for(int i=0; i<node2.dist/2; i++) id = prev[id];
        roots.add(id);
        if (node2.dist % 2 == 1) roots.add(prev[id]);
        return roots;
    }
}
class Node {
    int id = -1;
    int dist = Integer.MIN_VALUE;
}

方法二:动态规划。


public class Solution {
    private int[] length;
    private int[][] height;
    private List<Integer>[] graph;
    private void dfs(int parent, int current) {
        for(int next: graph[current]) {
            if (next == parent) continue;
            dfs(current, next);
            int h = height[next][0] + 1;
            if (h > height[current][0]) {
                height[current][1] = height[current][0];
                height[current][0] = h;
            } else if (h > height[current][1]) {
                height[current][1] = h;
            }
        }
    }
    private int min;
    private void dfs(int parent, int current, int longest) {
        length[current] = Math.max(longest, height[current][0]);
        if (length[current] < min) min = length[current];
        for(int next: graph[current]) {
            if (next == parent) continue;
            int nl = Math.max(longest+1, height[next][0]+1==height[current][0]? height[current][1]+1 : height[current][0]+1);
            dfs(current, next, nl);
        }
    }
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        List<Integer> roots = new ArrayList<>();
        if (n==0) return roots;
        if (n<=2) {
            for(int i=0; i<n; i++) roots.add(i);
            return roots;
        }
        min = n;
        length = new int[n];
        height = new int[n][2];
        graph = new List[n];
        for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
        for(int i=0; i<edges.length; i++) {
            graph[edges[i][0]].add(edges[i][1]);
            graph[edges[i][1]].add(edges[i][0]);
        }
        dfs(-1, 0);
        dfs(-1, 0, 0);
        for(int i=0; i<n; i++) {
            if (length[i] == min) roots.add(i);
        }
        return roots;
    }
}

优化,将参数定义为高度和半径,含义更清晰:

public class Solution {
    private int min = Integer.MAX_VALUE;
    private int[] radius;
    private int[][] height;
    private List<Integer>[] graph;
    private void calcHeight(int prev, int curr) {
        for(int i=0; i<graph[curr].size(); i++) {
            int next = graph[curr].get(i);
            if (next == prev) continue;
            calcHeight(curr, next);
            int h = height[next][0] + 1;
            if (h > height[curr][0]) {
                height[curr][1] = height[curr][0];
                height[curr][0] = h;
            } else if (h > height[curr][1]) {
                height[curr][1] = h;
            }
        }
    }
    private void calcRadius(int prev, int curr, int sum) {
        radius[curr] = Math.max(sum, height[curr][0]);
        if (radius[curr] < min) min = radius[curr];
        for(int i=0; i<graph[curr].size(); i++) {
            int next = graph[curr].get(i);
            if (next == prev) continue;
            int nextSum;
            if (height[next][0]+1 == height[curr][0]) nextSum = Math.max(sum, height[curr][1]) + 1;
            else nextSum = Math.max(sum, height[curr][0]) + 1;
            calcRadius(curr, next, nextSum);
        }
    }
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        radius = new int[n];
        height = new int[n][2];
        graph = new List[n];
        for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
        for(int[] edge: edges) {
            graph[edge[0]].add(edge[1]);
            graph[edge[1]].add(edge[0]);
        }
        calcHeight(-1, 0);
        calcRadius(-1, 0, 0);
        List<Integer> roots = new ArrayList<>();
        for(int i=0; i<n; i++) {
            if (radius[i] == min) roots.add(i);
        }
        return roots;
    }
}


啰嗦一点的方法:

public class Solution {
    private int[] radius;
    private int[][] height;
    private int[][] htnode;
    private List<Integer>[] graph;
    private void find(int prev, int node) {
        for(int next: graph[node]) {
            if (next == prev) continue;
            find(node, next);
            if (height[next][0] + 1 > height[node][0]) {
                height[node][0] = height[next][0] + 1;
                htnode[node][0] = next;
            }
        }
        for(int next: graph[node]) {
            if (next == prev) continue;
            if (next == htnode[node][0]) continue;
            if (height[next][0] + 1 > height[node][1]) {
                height[node][1] = height[next][0] + 1;
                htnode[node][1] = next;
            }
        }
    }
    
    private void find(int prev, int node, int sum) {
        radius[node] = Math.max(sum, height[node][0]);
        
        for(int next: graph[node]) {
            if (next == prev) continue;
            if (next == htnode[node][0]) {
                find(node, next, Math.max(sum+1, height[node][1]+1));
            } else {
                find(node, next, Math.max(sum+1, height[node][0]+1));
            }
        }
    }

    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        radius = new int[n];
        height = new int[n][2];
        htnode = new int[n][2];
        graph = new List[n];
        for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
        for(int[] edge: edges) {
            graph[edge[0]].add(edge[1]);
            graph[edge[1]].add(edge[0]);
        }
        
        find(-1, 0);
        find(-1, 0, 0);
        
        int min = Integer.MAX_VALUE;
        for(int r: radius) min = Math.min(min, r);
        List<Integer> roots = new ArrayList<>();
        for(int i=0; i<radius.length; i++) {
            if (radius[i] == min) roots.add(i);
        }
        return roots;
    }
}

参考文章:https://leetcode.com/discuss/72739/two-o-n-solutions

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值