[算法分析与设计] leetcode 每周一题: 310. Minimum Height Trees


题目链接: 

310. Minimum Height Trees




题目大意:

给定一个具有树的性质的无向图, 选择其中任意一个节点作为根节点即可生成一棵有根树, 所有不同的生成树中, 高度最小的那些便被称为 minimum height trees (MHTs), 现要求找出 给定无向图的 MHTs, 返回结果为 各MHT 的根节点(对应的 label) ;


格式说明:

给定的无向图共含有 n个节点, 各节点对应 0, 1, ..., n-1 的 label ;

输入为 n, 以及 无向边集(用 label 的对 表示边) edges ;


例如: 给定: n 4, edges = [ [1, 0], [1, 2], [1, 3] ], 则输出应为: [1] ;

又例: 给定: n = 6, edges = [ [0, 3], [1, 3], [2, 3], [4, 3], [5, 4] ], 则输出应为: [3, 4] ;

( 注: 还是看原题链接里的 Example 比较好, 有 示意图 ... )




解题过程:

(1) 考虑 MHTs 的高度都是相同的, 且原无向图已经可以看作一棵树, 所以直观上便可以直接由外往内推进, 即, 逐步将 最外围的节点(叶子节点) 摘去(也即, 生成树的高度+1), 最终剩下的(或 最后才被摘去的) 节点便可以被选作根节点 ;

(2) 然后可以考虑用 degree 来表示 节点的相互关系, 即, 最外围的节点肯定是 degree = 1 的 ;

(3) 这样, 就只需要考虑 处理好节点与节点之间的关系(如: 摘去一个节点, 其相邻节点也会有所改变, 但这些改变本身显然不应该对其他节点产生影响), 以免错乱, 即可 ;




代码如下:

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
        vector<int> degrees(n, 0);
        vector< unordered_set<int> > adjs(n, unordered_set<int>() );
        for (auto e : edges) {
            auto nodeA = e.first;
            auto nodeB = e.second;
            degrees[nodeA]++;
            degrees[nodeB]++;
            adjs[nodeA].insert(nodeB);
            adjs[nodeB].insert(nodeA);
        }
        
        vector<int> candidateLeafs;

        unordered_set<int> candidateRoots;
        for (size_t i = 0; i < n; i++) { candidateRoots.insert(i); }
        
        do {
            candidateLeafs.clear();
            for (size_t i = 0; i < degrees.size(); i++) {
                if (degrees[i] == 1) {
                    candidateLeafs.push_back(i);
                    candidateRoots.erase(i);
                }
            }
            for (auto i : candidateLeafs) {
                degrees[i]--;
                for (auto n : adjs[i]) {
                    degrees[n]--;
                    adjs[n].erase(i);
                }
            }
        } while (candidateRoots.size() > 0 && candidateLeafs.size() > 0);
        
        if (candidateLeafs.size() > 0) { return candidateLeafs; }
        
        vector<int> ret;
        for (auto r : candidateRoots) { ret.push_back(r); }
        return ret;
    }
};

(*) 这里实现得颇冗长, 比如, 在这里, 对一个节点来说, degree 和 相邻节点是有关联的 ; 而且, 考虑到其本身就已经是一棵树, 所以也可以直接利用 树 的特性, 比如, 叶子节点被摘去后新的叶子节点必然只可能是其本来的相邻节点, 等等 ;




Runtime: 102 ms


题目链接: 

310. Minimum Height Trees




题目大意:

给定一个具有树的性质的无向图, 选择其中任意一个节点作为根节点即可生成一棵有根树, 所有不同的生成树中, 高度最小的那些便被称为 minimum height trees (MHTs), 现要求找出 给定无向图的 MHTs, 返回结果为 各MHT 的根节点(对应的 label) ;


格式说明:

给定的无向图共含有 n个节点, 各节点对应 0, 1, ..., n-1 的 label ;

输入为 n, 以及 无向边集(用 label 的对 表示边) edges ;


例如: 给定: n 4, edges = [ [1, 0], [1, 2], [1, 3] ], 则输出应为: [1] ;

又例: 给定: n = 6, edges = [ [0, 3], [1, 3], [2, 3], [4, 3], [5, 4] ], 则输出应为: [3, 4] ;

( 注: 还是看原题链接里的 Example 比较好, 有 示意图 ... )




解题过程:

(1) 考虑 MHTs 的高度都是相同的, 且原无向图已经可以看作一棵树, 所以直观上便可以直接由外往内推进, 即, 逐步将 最外围的节点(叶子节点) 摘去(也即, 生成树的高度+1), 最终剩下的(或 最后才被摘去的) 节点便可以被选作根节点 ;

(2) 然后可以考虑用 degree 来表示 节点的相互关系, 即, 最外围的节点肯定是 degree = 1 的 ;

(3) 这样, 就只需要考虑 处理好节点与节点之间的关系(如: 摘去一个节点, 其相邻节点也会有所改变, 但这些改变本身显然不应该对其他节点产生影响), 以免错乱, 即可 ;




代码如下:

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
        vector<int> degrees(n, 0);
        vector< unordered_set<int> > adjs(n, unordered_set<int>() );
        for (auto e : edges) {
            auto nodeA = e.first;
            auto nodeB = e.second;
            degrees[nodeA]++;
            degrees[nodeB]++;
            adjs[nodeA].insert(nodeB);
            adjs[nodeB].insert(nodeA);
        }
        
        vector<int> candidateLeafs;

        unordered_set<int> candidateRoots;
        for (size_t i = 0; i < n; i++) { candidateRoots.insert(i); }
        
        do {
            candidateLeafs.clear();
            for (size_t i = 0; i < degrees.size(); i++) {
                if (degrees[i] == 1) {
                    candidateLeafs.push_back(i);
                    candidateRoots.erase(i);
                }
            }
            for (auto i : candidateLeafs) {
                degrees[i]--;
                for (auto n : adjs[i]) {
                    degrees[n]--;
                    adjs[n].erase(i);
                }
            }
        } while (candidateRoots.size() > 0 && candidateLeafs.size() > 0);
        
        if (candidateLeafs.size() > 0) { return candidateLeafs; }
        
        vector<int> ret;
        for (auto r : candidateRoots) { ret.push_back(r); }
        return ret;
    }
};

(*) 这里实现得颇冗长, 比如, 在这里, 对一个节点来说, degree 和 相邻节点是有关联的 ; 而且, 考虑到其本身就已经是一棵树, 所以也可以直接利用 树 的特性, 比如, 叶子节点被摘去后新的叶子节点必然只可能是其本来的相邻节点, 等等 ;




Runtime: 102 ms


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值