LC310. 最小高度树

题目

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

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

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

请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。
树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。

提示:

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

示例 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]

思路

这题看起来很简单,不就是求最小高度树嘛,依次求每个节点高度不就完了,bfs一顿写后,结果发现时间复杂度超了…
n的范围在那摆着,n2级的时间复杂度肯定玩完。

那怎么能缩小时间复杂度呢?
首先我们应该明确的一点是:想要计算树的高度只有bfs和dfs两种方式
但是计算每个节点的高度肯定超时,那么我们可不可以寻找一种方式,不需要计算高度呢。

使劲看案例中的图像,首先可以一点:只有一条边的节点做节点时一定不是最短高度树(n>2),这样就可以说他是一个叶子节点,是一个边界。
借此思路,我们沿着边界的点往里收缩,可以发现中间的节点好像离其他所有点都比较近。
这就可以判定从边为1的点开始往图中心收缩,最中间的点作为根的树一定是最小生成树。
怎么收缩?从叶子节点开始往里层次遍历(bfs)
需要建立数据结构:

  • ans:保存当前最小距离节点
  • umap:邻接表,记录每个点的邻接点
  • E:记录每个点的度(相连边的数量)
  • q:队列,用于bfs

具体流程如下:

  • 将所有节点关系存储在邻接表umap中,度存储在E中
  • 将度为1的节点入队
  • 进行层次遍历:直到寻找到最中间的点
  • 输出

代码

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        if(n==1) return {0};
        vector<int> ans;
        unordered_map<int, vector<int>> umap;
        vector<int> E(n);
        for(auto e:edges){
            E[e[0]]++;
            E[e[1]]++;
            umap[e[0]].push_back(e[1]);
            umap[e[1]].push_back(e[0]);
        }
        queue<int> q;
        for(int i=0;i<n;i++){
            if(E[i]==1)
                q.push(i);
        }
        //层次遍历
        while(!q.empty()){
            ans.clear();
            int size = q.size();
            //遍历当前层节点
            for(int i=0;i<size;i++){
                int t = q.front();
                q.pop();
                ans.push_back(t); //暂且将该层节点当作最中间的节点
                //遍历该节点的邻居节点
                for(auto x:umap[t]){
                    E[x]--;  //意思是t与x相连的边可以不用考虑了,因为上面用过了
                    if(E[x]==1)  
                        q.push(x);  //入队
                }
            }
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值