310. Minimum Height Trees

输入:包含n个节点的无向图。n:表示从0到n-1,n个节点。edges:int数组,是从一个节点到另外一个节点。但是没有方向。
输出:以哪些节点为根节点,具有最小高度的树,返回这些根节点。
规则:一个树的高度是指从根节点到叶子节点的最远路径。最小高度树,是指所有树中高度最小的树。
分析:当只有一个节点的时候,只要返回节点0就可以。
在这里插入图片描述
 当有两个节点且相连的时候,哪个节点做根节点,树的高度都相同。
 在这里插入图片描述
 当有3个节点的时候,node1和node3肯定不能为根节点,因为以node2为根,高度是1;而以node1或者node3高度为2。也就是说当有3个或者以上节点的时候,使用叶子节点做根节点的树肯定不符合要求。假设叶子节点nodeA为根是高度最小树,那么nodeA肯定有子节点还有其他节点,否则一棵树不能包含所有节点。那么以nodeA的子节点为根,高度减1,与假设不符合。有了这个结论:使用叶子节点做根节点的树肯定不符合要求。(这个观点的来源)我们就可以使用动态规划的思路来不断解决更小的问题。
 在这里插入图片描述
 例如上图5个节点的图,node4,node5不是根节点,那谁是呢?把node4,node5从图中移除,只剩下node1,node2,node3,3个节点。
 在这里插入图片描述
 在这3个节点的图中,node1,node3肯定不是根节点。将这两个节点从图中移除。就只有node2。也就是说node2是根节点。
 在这个问题规模不断变小的过程中,不断去掉叶子节点。而这个操作不影响原有问题的答案。

public List<Integer> findMinHeightTrees(int n, int[][] edges) {

        Map<Integer,List<Integer>> graph = createGraph(edges,n);
        int[] inDegrees = new int[n];
        for(int[] edge : edges){
            inDegrees[edge[0]]++;
            inDegrees[edge[1]]++;
        }
        List<Integer> result = new ArrayList<Integer>();
        Queue<Integer> queue = new LinkedList<Integer>();
        for(int i=0;i<n;i++){
            if(inDegrees[i]==0){
                result.add(i);
                return result;
            }else if(inDegrees[i]==1){
                queue.offer(i);
            }
        }
        while(!queue.isEmpty()){
            result = new ArrayList<Integer>();
            int size = queue.size();
            for(int i=0;i<size;i++){
                int node = queue.poll();
                result.add(node);
                for(Integer toNode : graph.get(node)){
                    inDegrees[toNode]--;
                    if(inDegrees[toNode]==1){
                        queue.offer(toNode);
                    }
                }
            }
        }
        return result;
    }

    private Map<Integer,List<Integer>> createGraph(int[][] edges,int n){
        Map<Integer,List<Integer>> graph = new HashMap<Integer,List<Integer>>();
        for(int i=0;i<n;i++){
            graph.put(i,new ArrayList<Integer>());
        }
        for(int[] edge : edges){
            graph.get(edge[0]).add(edge[1]);
            graph.get(edge[1]).add(edge[0]);
        }
        return graph;
    }

分析2:分别以每个节点作为根节点计算树的高度,在这个过程中比较记录最小高度minHeight。遍历之前的结果,等于minHeight的节点加入到结果集中。
 在计算树的高度的过程中,可以使用动态规划的想法。假设已知 A 的所有相邻节点分别为树根的各个子树的树高,那么 A根的树高等于 已知的各个子树树高中的最大值 加一。方程式表达如下,即状态转换方程:
height(A) = max(height(A.next0), height(A.next2),… height(A.nextk)) + 1(来源网址
在计算过程中需要记录各个子树的高度,避免重复计算。缓存的key值是“当前节点->子节点”。

private Map<String,Integer> cache = new HashMap<String,Integer>();
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        Map<Integer,List<Integer>> graph = createGraph(edges,n);
        int[] heights = new int[n];
        int minHeight = n;
        for(int i=0;i<n;i++){
            heights[i] = findHeight(graph,i,-1);
            minHeight = Math.min(minHeight,heights[i]);
        }
        List<Integer> result = new ArrayList<Integer>();
        for(int i=0;i<n;i++){
            if(heights[i]==minHeight){
                result.add(i);
            }
        }
        return result;
    }
    private int findHeight(Map<Integer,List<Integer>> graph,int root,int parent){
        int height = 0;
        for(Integer toNode : graph.get(root)){
            if(toNode==parent){
                continue;
            }
            String key = String.valueOf(root)+"->"+toNode;
            int tmp;
            if(cache.get(key)!=null){
                tmp = cache.get(key);
            }else{
                tmp = findHeight(graph,toNode,root);
                cache.put(key,tmp);
            }
            height = Math.max(height,tmp);
        }
        return height+1;
    }

    private Map<Integer,List<Integer>> createGraph(int[][] edges,int n){
        Map<Integer,List<Integer>> graph = new HashMap<Integer,List<Integer>>();
        for(int i=0;i<n;i++){
            graph.put(i,new ArrayList<Integer>());
        }
        for(int[] edge : edges){
            graph.get(edge[0]).add(edge[1]);
            graph.get(edge[1]).add(edge[0]);
        }
        return graph;
    }

代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值