0
点赞
收藏
分享

微信扫一扫

leetcode 310. 最小高度树

三千筱夜 2022-04-06 阅读 70

题目链接
思路:深搜
分析:这个题目要找最小高度树,要达到最小高度,要处理的其实是最长路径,如果我们找到了最长的路径,那么只要取中间的节点当作根节点就是最小高度树,因为最长的路径被中分了,那么也就可以做到高度最小了。
也就是说,在这个图里面找到一根最长的线,那么,你怎么折这根线使得折完以后,这根最长的线最短。那么肯定是对折,对吧。
那么现在有两个问题:

  1. 如果找到这个图里面的最长路径,也就是如何找到这根最长的线。
  2. 找到最长路径后,如何找到这个路径的中点。

**问题1:**我们可以这么想,把自身当作里面的一个点,先从自己出发,找到距离自己最远的点,这个点肯定是最长路径的一个端点。简单证明一下:如下图。
在这里插入图片描述
设AB就是要寻找的那个最长路径,当然,这里的A,B两个点目前是不知道的,但是肯定存在这样两个点确定这条最长的路径。
设当前点为C,
设BP=x,AP=t,PC=Y。
也就是说t+x肯定是所有路径里面最长的路径。
那么,我们假设,距离C最远的点是D点,如下图。
在这里插入图片描述
假设DP=s,如果存在D点,距离C最远
即y+s>y+max(t,x);
即 s>max(t,x);
max(t,x)=t或者x

如果max(t,x)=x
那么t+s>t+x

如果max(t,x)=t
x+s>x+t

那么就违背了t+x是最长的路径的前提
所以不存在这样的D点,使得C距离最远的点是D。
所以距离C点最远的点肯定是A,B中的一点
所以从任意C点出发,最远的点肯定是最长路径的一个端点。
证明完毕。

那么我们找到了最长路径的一个端点后,再从这个点出发,距离最远的点肯定是这个最长路径的另外一个点。
那么也就找到了最长路径。(因为我们找到了两个端点,两点确定一条直线,不对,曲线把,就那个意思,理解一下)。
到此问题一解决。

**问题二:**找到最长路径后,如果找到中点。这里是将这条最长路径经过的每个节点记录下来,那么直接取中点就行。
从一个节点开始去寻找最远节点的时候,采用的是广度优先遍历,可以用一个数组parentArr来记录每个节点的父节点。
即parentArr[i]=x:表示节点i的父节点是x。
当我们找到最长路径的一个端点,从这个端点出发,去找另一个端点的时候,也就是把这个端点当作根节点,去找最远的叶节点。那么这一趟我们记录好每个节点的父节点即可。根节点的父节点记作-1.
那么当我们找到另一个端点的时候,从这个端点开始,反向一层层找父节点,一直找到根节点为止,路径也就找到了。

代码实现:

class Solution {
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        List<Integer> ans = new ArrayList<>();
        if(n==1){
            ans.add(0);
            return ans;
        }

        //list[x]:表示以x为出发点可以直接达到的点的链表,也就是邻接链表
        List<Integer>[] list=new ArrayList[n];
        for(int i=0;i<n;i++){
            list[i]=new ArrayList<>();
        }
        for(int[] edge:edges){
            list[edge[0]].add(edge[1]);
            list[edge[1]].add(edge[0]);
        }
        int[] parentArr = new int[n];
        Arrays.fill(parentArr,-1);

        
        int x=findLongPathBfs(0,list,n,parentArr);
        //将当前节点当作根节点,根节点的父节点为-1
        parentArr[x]=-1;
        //y就是另一个端点
        int y=findLongPathBfs(x,list,n,parentArr);
        //x->y最长
        //path记录从y出发到x经过的每个点
        List<Integer> path=new ArrayList<>();
        //寻找从y走到x的路径
        while(parentArr[y]!=-1){
            path.add(y);
            y=parentArr[y];
        }
        path.add(y);
        int accountPoint = path.size();

        //如果最长的路径中有奇数个点,那么最小高度数的根节点就是最中间的点
        ans.add(path.get(accountPoint/2));

        //如果最长的路径中有偶数个点,那么最小高度数的根节点有两个
        if((accountPoint%2)==0){
            ans.add(path.get(accountPoint/2-1));
        }
        return ans;
    }

    //广度优先遍历 从start开始,从邻接矩阵中寻找距离最远的点
    public int findLongPathBfs(int start, List[] list, int n, int[] parentArr){
        Queue<Integer> parent = new LinkedList<>();
        boolean[] vis = new boolean[n];
        parent.offer(start);
        
        //用来记录最远的节点
        int maxLenPoint=start;
        
        //广度优先遍历
        while(!parent.isEmpty()){
            int cur=parent.poll();
            maxLenPoint=cur;
            vis[cur]=true;
            if(list[cur]!=null){
                List<Integer> children = list[cur];
                for(Integer child : children){
                    if(!vis[child]){
                        parent.offer(child);
                        parentArr[child] = cur;
                    }
                }
            }
        }
        
        return maxLenPoint;
    }
}
举报

相关推荐

0 条评论