【Leetcode】310. 最小高度树

QUESTION

medium

题目描述

对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点。

格式:

  • 该图包含 n 个节点,标记为 0n - 1。给定数字 n 和一个无向边 edges 列表(每一个边都是一对标签)。
  • 你可以假设没有重复的边会出现在 edges 中。由于所有的边都是无向边,[0, 1] 和 [1, 0] 是相同的,因此不会同时出现在 edges 里。

示例 1:

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

        0
        |
        1
       / \
      2   3

输出: [1]

示例 2:

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

     0  1  2
      \ | /
        3
        |
        4
        |
        5

输出: [3, 4]

说明

  • 根据树的定义,树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。
  • 树的高度是指根节点和叶子节点之间最长向下路径上边的数量。

SOLUTION

一开始我写了一个很低效的方法(不能 AC,如下),这个方法计算每个节点作为根时其树的高度。通过计算出所有节点的高度,输出最小高度的情况的答案。但是超出时限(几百个点的图,算爆了)。

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
        unordered_map<int, vector<int>> m;
        vector<int> res;
        int min = INT_MAX;
        for(auto e : edges){
            m[e.first].push_back(e.second);
            m[e.second].push_back(e.first);
        }
        for(int i = 0; i < n; i++){
            int currH = getHeight(i, m);
            if(currH < min){
                res.clear();
                res.push_back(i);
                min = currH;
            }
            else if(currH == min){
                res.push_back(i);
            }
        }
        return res;
    }
    int getHeight(int node, unordered_map<int, vector<int>>& m) {
        set<int> checked;
        queue<int> q;
        int h = -1;
        q.push(node);
        checked.insert(node);
        while(!q.empty()){
            h++;
            int qsize = q.size();
            while(qsize){
                qsize--;
                int curr = q.front(); q.pop();
                for(auto e : m[curr]) if(checked.insert(e).second) q.push(e);
            }
        }
        return h;
    }
};

经过对以上代码一系列的优化,仍然超出时限,看来这样的方法并不可取。(有 DFS 通过的,没有尝试)

方法一

实则无奈,看了 Leetcode 的评论,得到了一种巧妙的方法:

  1. 首先构建一个图
  2. 然后剥掉最外面的一层(删除当前图所有的叶子节点,叶子节点即只有一条连边的节点)
  3. 重复步骤 2,直至只剩下 1 个或 2 个节点
class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
        if(n == 1) return {0};
        vector<unordered_set<int>> g(n); //这是一个图,保存了 g[i] 可到的下一个点的集合
        vector<int> res;
        queue<int> q; //用于保存叶节点的队列
        for(auto e : edges){ //构造图
            g[e.first].insert(e.second);
            g[e.second].insert(e.first);
        }
        for(int i = 0; i < n; i++){ //找叶节点
            if(g[i].size() == 1) q.push(i);
        }
        while(n > 2) {
            int size = q.size();
            n -= size;
            for(int i = 0; i < size; ++i) {
                int t = q.front(); q.pop();
                for(auto e : g[t]) {
                    g[e].erase(t);
                    if(g[e].size() == 1) q.push(e);
                }
            }
        }
        while(!q.empty()) {
            res.push_back(q.front()); q.pop();
        }
        return res;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值