310. Minimum Height Trees

仅供自己学习

 

思路:

最容易想到的就是对每个点DFS并记录长度,但是会超时,所以只能对数据进行剪枝处理。

这个解法必须明确是在对树操作。我们将树中度为1的结点去掉,是同时去掉,因为这样不会改变树整体的长度比较的结果,相当于每个节点作为根节点的长度-1,如果不能同时去掉,那么就会导致长度的改变,这样就并不能等同于对原问题的数据进行求解了。因为是树,度为1的时候只可能为边缘结点,(此时讨论树时不认为有根节点,那么就不能说叶子节点),(根节点可能度为1,因为并不是一定二叉树),所以我们是一层一层向里靠近,我们不断从最外部开始删除边缘结点,即去除度为1的结点,直到结点数小于等于2后,就结束这个过程,此时剩下的结点就是想要的根节点。

这样的解法为什么会正确,当我们要找最小的树长,选取的根节点应该能做到尽量保证左右两侧的长度是平衡的,否则必有一侧更长一侧更短,这样当我们一层一层剥开边缘结点后剩下的结点就是能起到平衡两侧长度的结点,因为可以看成两个节点看成一个圆心,这个圆不断以相同的长度变小,他才能还是个圆而不能是椭圆。

还可用反证法证明正确性,如果A树为最短树长树,B树为A树去掉度为1的结点得到的树,如果B树不是最小树,那么还存在最小树,令此时B树另一个节点做根节点的话,为最小树B'树,那么B‘树加上原来去掉的度为1的结点得到的树 A’,与A不是同一个根节点形成的树,那么与A树为最短树长树相冲突;又因为 A树,B树为最短树长树时,根节点相同,所以去掉的边缘结点不会是根节点,故该方法正确。

 

代码:

class Solution {
public:

    
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        vector<vector<int>> graph(n);
        vector<vector<int>> borders(2); //设边缘结点的集合为两个,交替使用
        vector<int> degree(n);
        if(n==1) return {0};
        if(n==2) return {0,1};
        for(const auto& u:edges){  //将数据等效为图,并记录每个结点的度
            graph[u[0]].emplace_back(u[1]);
            graph[u[1]].emplace_back(u[0]);
            ++degree[u[0]];
            ++degree[u[1]];
        }
        for(int i=0;i<n;++i){
            if(degree[i]==1){
                borders[0].emplace_back(i);
            }
        }
        int sw=0;
        while(1){
            borders[reverse(sw)].clear();  //这里的边缘结点集合交替使用,一个使用,另一个清空后用于装填新的边缘节点
            for(auto bor:borders[sw]){
                for(auto neighbor:graph[bor]){
                    --degree[neighbor];
                    if(degree[neighbor]==1)
                        borders[reverse(sw)].emplace_back(neighbor);
                }
            }
            if(borders[reverse(sw)].empty()) break; //当没有满足条件的边缘节点加入后就结束,此时另一个边缘集合是有边缘节点集合的
            sw=reverse(sw);
        }
        return borders[sw];
    }
private:
    int reverse(int x)
    {
        if(x==0)return 1;
        else return 0;
    }

};

 

posted @ 2021-02-28 21:30  Mrsdwang  阅读(43)  评论(0)    收藏  举报