310. Minimum Height Trees

来源:互联网 发布:c语言读取jpg图片 编辑:程序博客网 时间:2024/06/07 10:48

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]

Show Hint 

    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.



    思路:又遇上了最痛苦的topological sort,赶紧补一下,http://blog.csdn.net/dm_vincent/article/details/7714519

    其实还是比较简单的,就想着 “学某门课要先修某门课” 这个实际的例子,把indegree为0的先拿出来,也就是不需要任何先修课的课程可以先学,然后更新其他节点的indegree,因为已经修了一门了嘛,说不定可以修其他以此为基础的课程呢!

    拓扑排序有2中实现方式:

    (1)BFS(就是上面描述的)

    (2)DFS (就是Coursera上algorithm课程老师描述的)


    类似的题目:

    https://leetcode.com/problems/course-schedule/?tab=Description

    https://leetcode.com/problems/course-schedule-ii/?tab=Description(只记录degree计数即可)


    这里当做无向图来对待:

    import java.util.ArrayList;import java.util.Collections;import java.util.HashSet;import java.util.List;/* * 找到最长的那条path,中间的1个或者2个就是要找的 * 参考拓扑排序的BFS实现 * 如果有1条最长的path,那不断减掉degree为1的,最后剩下的就是 * 如果有2条最长的path,那他们必然相交于中点,如果不是,就必然可以找到更长的path,相交于中点的话,不断减掉degree为1的仍然是可行的 */public class Solution {    public List<Integer> findMinHeightTrees(int n, int[][] edges) {        if(n == 1)return Collections.singletonList(0);        // 构建degree集合,因为要最后的结果,所以不能简单用一个数组对degree计数    List<HashSet<Integer>> degree = new ArrayList<HashSet<Integer>>();    for(int i=0; i<n; i++)degree.add(new HashSet<Integer>());        for(int[] edge : edges) {    degree.get(edge[0]).add(edge[1]);    degree.get(edge[1]).add(edge[0]);    }            // 不断删除degree为0的节点,再更新剩下的节点的degree,所以用一个数组保存当前degree为0的节点    List<Integer> leaves = new ArrayList<Integer>();    for(int i=0; i<n; i++)     if(degree.get(i).size() == 1)    leaves.add(i);        // 一直往中点收缩,剩下个1就是1个,剩下2个就是2个    while(n > 2) {        n = n - leaves.size();    List<Integer> newLeaves = new ArrayList<Integer>();        // 更新degree    for(int leaf : leaves) {    for(int j : degree.get(leaf)) {    degree.get(j).remove(leaf);        // 不可能出现degree为0,    if(degree.get(j).size() == 1)    newLeaves.add(j);    }    }        leaves = newLeaves;    }        return leaves;    }}


    二刷:

    看了一下第一次刷写的博客,感觉完全不一样,还是要多刷,不然都找不到题目的感觉

    这次刷因为有一刷的概念,直接想到了从两边收索找中点的思路,写了以下:

    package l310;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;/* * last case TLE */public class ArrayBFS {    public List<Integer> findMinHeightTrees(int n, int[][] edges) {    int[] degree = new int[n];    boolean[][] adj = new boolean[n][n];    for(int[] edge : edges) {    degree[edge[0]] ++;    degree[edge[1]] ++;    adj[edge[0]][edge[1]] = true;    adj[edge[1]][edge[0]] = true;    }        int cnt = 0;    while(cnt < n-2) {    Set<Integer> s = new HashSet<Integer>();    for(int i=0; i<n; i++)        if(degree[i] == 1) {        degree[i] = -1;        s.add(i);        }        for(int i : s)    for(int j=0; j<n; j++)    if(adj[i][j])degree[j] --;        cnt += s.size();    }        List<Integer> ret = new ArrayList<Integer>();    for(int i=0; i<n; i++)    if(degree[i] >= 0)ret.add(i);    return ret;    }}

    猜想可能是因为是稀疏矩阵,用连接表可能会快一点

    package l310;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;/* * last case TLE * so we use Map or List, notice we will always have 0 .. n-1, so we just use List * 还可以优化,求degree为1的节点的时候不需要遍历,只要每当有degree为1就加到一个Set里面(这好像也是拓扑排序的优化技巧) */public class Solution {    public List<Integer> findMinHeightTrees(int n, int[][] edges) {    int[] degree = new int[n];    List<List<Integer>> adj = new ArrayList<List<Integer>>();    for(int i=0; i<n; i++)adj.add(new ArrayList<Integer>());    for(int[] edge : edges) {    degree[edge[0]] ++;    degree[edge[1]] ++;    adj.get(edge[0]).add(edge[1]);    adj.get(edge[1]).add(edge[0]);    }        int cnt = 0;    while(cnt < n-2) {    Set<Integer> s = new HashSet<Integer>();    for(int i=0; i<n; i++)        if(degree[i] == 1) {        degree[i] = -1;        s.add(i);        }        for(int i : s)    for(int j : adj.get(i))    degree[j] --;        cnt += s.size();    }        List<Integer> ret = new ArrayList<Integer>();    for(int i=0; i<n; i++)    if(degree[i] >= 0)ret.add(i);    return ret;    }}


    看到有人说暴力也可以AC,就试了一下遍历每个节点当root求树高,再取最小值

    package l310;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/* * TLE */public class BruteForce {    public List<Integer> findMinHeightTrees(int n, int[][] edges) {        Map<Integer, List<Integer>> m = new HashMap<Integer, List<Integer>>();        for(int[] edge : edges) {        if(!m.containsKey(edge[0]))m.put(edge[0], new ArrayList<Integer>());        m.get(edge[0]).add(edge[1]);        if(!m.containsKey(edge[1]))m.put(edge[1], new ArrayList<Integer>());        m.get(edge[1]).add(edge[0]);        }                int min = Integer.MAX_VALUE;        List<Integer> ret = new ArrayList<Integer>();        ret.add(0);                for(int root : m.keySet()) {        boolean[] marked = new boolean[n];        marked[root] = true;        int h = getHeight(m, root, marked);        if(h < min){        min = h;        ret.clear();        ret.add(root);        } else if(h == min) {        ret.add(root);        }        }                return ret;    }private int getHeight(Map<Integer, List<Integer>> m, int root, boolean[] marked) {int ret = 0;for(int next : m.get(root))if(!marked[next]) {marked[next] = true;ret = Math.max(ret, 1 + getHeight(m, next, marked));marked[next] = false;}return ret;}}

    也试了一下Floyd算法

    package l310;import java.util.ArrayList;import java.util.Arrays;import java.util.List;/* * Floyd * 某一行最大也不超过最大的一半就是可能的root */public class Floyd {    public List<Integer> findMinHeightTrees(int n, int[][] edges) {        // Floyd    int[][] adj = new int[n][n];    for(int[] a : adj)Arrays.fill(a, Integer.MAX_VALUE);        for(int[] edge : edges) {        adj[edge[0]][edge[1]] = 1;        adj[edge[1]][edge[0]] = 1;        }        for(int k=0; k<n; k++)        for(int i=0; i<n; i++)        for(int j=0; j<n; j++)        if(adj[i][k]!=Integer.MAX_VALUE && adj[k][j]!=Integer.MAX_VALUE         && i!=j && adj[i][k] + adj[k][j] < adj[i][j])         adj[i][j] = adj[i][k] + adj[k][j];                //         int[] max = new int[n];        for(int i=0; i<n; i++)        for(int j=0; j<n; j++)        if(adj[i][j] != Integer.MAX_VALUE)        max[i] = Math.max(max[i], adj[i][j]);        int allMax = 0;        for(int a : max)        if(a != Integer.MAX_VALUE)allMax = Math.max(allMax, a);        int validMax = (allMax+1)/2;                List<Integer> ret = new ArrayList<Integer>();        for(int i=0; i<n; i++)        if(max[i] <= validMax)ret.add(i);        return ret;}}


    但是这些暴力解答都没有AC



    0 0
    原创粉丝点击