leetcode 310. 最小高度树

树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。

给你一棵包含 n 个节点的数,标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表(每一个边都是一对标签),其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条无向边。

可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树 。

请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。

树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。
leetcode 310. 最小高度树
示例 1:
在这里插入图片描述

输入:n = 4, edges = [[1,0],[1,2],[1,3]]
输出:[1]
解释:如图所示,当根是标签为 1 的节点时,树的高度是 1 ,这是唯一的最小高度树。

示例 2:
在这里插入图片描述

输入:n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
输出:[3,4]

提示:

  • 1 <= n <= 2 * 104
  • edges.length == n - 1
  • 0 <= ai, bi < n
  • ai != bi
  • 所有 (ai, bi) 互不相同
  • 给定的输入 保证 是一棵树,并且 不会有重复的边

方法一:bfs
统计各节点的度,然后把所有出度为1的节点进队列,即先将叶子结点入队列,然后不断地bfs,最后找到的就是由两边的叶子结点同时向中间靠近的节点,那么这个中间节点就相当于把整个树的最大直径平分了。

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        //邻接表
        vector<vector<int>> adj(n);
        //入度表
        vector<int> degree(n, 0);
        //结果集
        vector<int> res;
        //初始化邻接表,统计入度
        for(auto edge : edges){
            adj[edge[0]].push_back(edge[1]);
            adj[edge[1]].push_back(edge[0]);
            degree[edge[0]]++;
            degree[edge[1]]++;
        }
        queue<int> qu;
        //将入度为1的叶子结点入队
        for(int i=0; i<n; i++){
            if(degree[i] == 1){
                qu.push(i);
            }
        }
        //特殊情况处理
        if(n == 1) res.push_back(0);
        
         //从外向内一层一层剥,每次加入的都是树的一层叶子,
         //最后一层即为最终结果
        while(!qu.empty()){
            int num = qu.size();
            res.clear();
            //将一层的叶子加入结果集,但在此之前先清空结果集
            for(int i=0; i<num; i++){
                int first = qu.front();
                qu.pop();
                res.push_back(first);
                for(int w : adj[first]){
                    degree[w]--;
                    //入度减为1的,变成叶子,入队
                    if(degree[w] == 1){
                        qu.push(w);
                    }
                }
            }
        }
        return res;
    }
};

方法二:dfs
在评论区看到的,题解中大部分都是bfs,这个dfs方法看了大半天才看明白,害~
思路:利用树的直径,找到中间点。
任意一点x出发,找到距离x最远的a点,然后从a点出发,找到距离a点最远的b点, 找到a和b之间的中点,即为所求。很明显可能是1个或者两个点

class Solution {
public:
    //邻接表
    vector<vector<int>> adj;
    //用来标记0 ~ n-1, n个节点分别到某个节点x的距离,
    vector<int> range;
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        adj.resize(n);
        range.resize(n, 0);
        //用来存放a和b之间的节点的辅助数组
        vector<int> help;
        for(auto edge : edges){
            adj[edge[0]].push_back(edge[1]);
            adj[edge[1]].push_back(edge[0]);
        }
        //第一次从0开始搜索,所以将0到0的距离置为1,以0为原点
        //用两点之间节点的数量表示两点的距离
        range[0] = 1;
        int a = dfs(0, range);
        //重置range的每一项为0
        fill(range.begin(), range.end(), 0);
        //第二次从a开始
        range[a] = 1;
        //此时,range是以a为原点,各个点距离a点的距离数组
        int b = dfs(a, range);
        help.push_back(b);
        //令a不动,b逐渐向a靠近
        while(a != b){
            for(int ans : adj[b]){
                //如果相邻的某一点ans距离a的距离 = b距离a的距离-1
                //说明a,ans,b在同一条线上
                if(range[ans]+1 == range[b]){
                    b = ans;
                    help.push_back(b);
                    //邻接表中应该只有一个满足
                    break;
                }
            }
        }

        vector<int> res;
        int mid;
        //辅助数组的size是2的倍数,中间点有两个
        if(help.size()%2 == 0){
            mid = help.size()/2;
            res.push_back(help[mid-1]);
            res.push_back(help[mid]);
        }
        else{
            mid = help.size()/2;
            res.push_back(help[mid]);
        }
        return res;
    }
    
    //深度搜索以v为根节点的树,距离v点最远的点
    int dfs(int v, vector<int>& range){
        int max = v;
        for(int w : adj[v]){
            //range[w]==0,表明还未被搜索过
            if(range[w] == 0){
                //因为是邻接点,所以在根节点的基础上+1
                range[w] = range[v]+1; 
                //以w为根节点,因为range[v]!=0 ,相当于断开v-w这条边
                int other = dfs(w, range);
                if(range[other] > range[max]){
                    max = other;
                }
            }
        }
        return max;
    }
};

答案整理,来源于题解和评论区。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值