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

来源:互联网 发布:cv source数据库 复旦 编辑:程序博客网 时间:2024/05/20 06:50

原题网址: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 0