leetcode 310. Minimum Height Trees

For an 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 :

Input: n = 4, edges = [[1, 0], [1, 2], [1, 3]]

        0
        |
        1
       / \
      2   3 

Output: [1]

Example 2 :

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

     0  1  2
      \ | /
        3
        |
        4
        |
        5 

Output: [3, 4]

Note:

  • 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.”
  • The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.

 

思路:第一感是dfs,思路类似https://leetcode.com/problems/sum-of-distances-in-tree/ 

先从0开始dfs,得到后面的结点n到叶节点的长度(只需要维护前2长的长度,方向0—>i),存到route[i][2]数组中。

route[i][2]用于后面求某节点i到所有叶结点的最长深度。包括方向i—>0。

为什么要维护每个节点到叶结点前2长的长度?因为后面求节点i到所有叶结点的最长深度(这一步可以bfs或dfs),要基于i的前一个节点j的前2的长度,因为j最长那条可能就在j—>i的方向,以此类推(求此长度是沿着方向0—>i)。这一步还要注意一些细节。

最后返回最长长度的两个节点。时间复杂度O(n),空间复杂度O(n).

方法一.dfs(11ms)

class Solution {
    int len;
    ArrayList<Integer> res;
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        res=new ArrayList<>();
        ArrayList<Integer>[] graph=(ArrayList<Integer>[])new ArrayList[n];
        for(int i=0;i<graph.length;i++) graph[i]=new ArrayList<Integer>();
        for(int[] e:edges){
            graph[e[0]].add(e[1]);
            graph[e[1]].add(e[0]);
        }
        int[][] route=new int[n][2];//存node节点前2大的距离
        boolean[] mark=new boolean[n];
        dfs(graph,0,mark,route);
        LinkedList<Integer> queue=new LinkedList<>();
        queue.add(0);
        len=route[0][0];
        res.add(0);
        mark[0]=false;
        while(queue.size()>0){
            int node=queue.poll();
            for(Integer m:graph[node]){
                if(mark[m]){
                    mark[m]=false;
                    if(route[node][0]-1>route[m][0]){//父节点在另外一条路径,这条路径之后的排除
                        continue;
                    }
                    // =的情况
                    queue.add(m);
                    if(route[node][1]>=route[m][0]){//route[node][1]另外一条路
                        route[m][1]=route[m][0];
                        route[m][0]=route[node][1]+1;
                    }else if(route[node][1]>=route[m][1]){
                        route[m][1]=route[node][1]+1;
                    }
                    
                    if(route[m][0]==len) res.add(m);
                    else if(route[m][0]<len){
                        res.clear();
                        len=route[m][0];
                        res.add(m);
                    }
                }
            }
        }
        return res;
    }
    int dfs(ArrayList<Integer>[] graph,int node,boolean[] mark,int[][] route){
        mark[node]=true;
        for(int n:graph[node]){
            if(!mark[n]){
                int a=dfs(graph,n,mark,route);
                if(a+1>=route[node][0]){
                    route[node][1]=route[node][0];
                    route[node][0]=a+1;
                }else if(a+1>route[node][1]){
                    route[node][1]=a+1;
                }
            }
        }
        return route[node][0];
    }
}

看了评论区,有一种很巧妙的思路。从所有叶结点开始bfs,每次消去一层叶结点。最后剩余的就是要返回的节点了。感觉这种思路挺难想到的。

方法二:bfs(40ms) 。大概是要用hashset存每条边,计算hash值比较耗时吧,应该还能优化。

class Solution {
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        List<Set<Integer>> graph=new ArrayList<>();
        List<Integer> leaves=new ArrayList<>();
        for(int i=0;i<n;i++) graph.add(new HashSet<Integer>());
        for(int[] e:edges){
            graph.get(e[0]).add(e[1]);
            graph.get(e[1]).add(e[0]);
        }
        for(int i=0;i<n;i++){
            if(n==1||graph.get(i).size()==1) leaves.add(i);//包括当只有一个元素时的边界情况
        }
        int size=n;
        while(size>2){//最后剩小于2个
            size-=leaves.size();
            List<Integer> newleaves=new ArrayList<>();
            for(int leaf:leaves){
                int node=graph.get(leaf).iterator().next();//set中第一个数,就是下一个节点(其实此时set中只有一个数)
                graph.get(node).remove(leaf);//后一个节点移除当前节点
                if(graph.get(node).size()==1) newleaves.add(node);
            }
            leaves=newleaves;
        }
        return leaves;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值