1. 程式人生 > 其它 >310最小高度樹(拓撲排序)

310最小高度樹(拓撲排序)

技術標籤:LeetCodeleetcode演算法

1、題目描述

樹是一個無向圖,其中任何兩個頂點只通過一條路徑連線。 換句話說,一個任何沒有簡單環路的連通圖都是一棵樹。

給你一棵包含n個節點的數,標記為0到n - 1 。給定數字n和一個有 n - 1 條無向邊的 edges列表(每一個邊都是一對標籤),其中 edges[i] = [ai, bi] 表示樹中節點 ai 和 bi 之間存在一條無向邊。

可選擇樹中任何一個節點作為根。當選擇節點 x 作為根節點時,設結果樹的高度為 h 。在所有可能的樹中,具有最小高度的樹(即,min(h))被稱為 最小高度樹 。

請你找到所有的 最小高度樹 並按 任意順序 返回它們的根節點標籤列表。

樹的 高度 是指根節點和葉子節點之間最長向下路徑上邊的數量。

2、示例

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

3、題解

解法一:

基本思想:最正常的思維然而超時,遍歷0~n-1每個節點,以該節點為根節點,不斷遞迴計算以此節點為根節點的樹的最大高度depth,如果depth小於mindepth,說明以此節點為根節點的樹的高度是目前樹高度中最小的,更新mindepth,儲存此節點到res。

解法二:

基本思想:拓撲排序思路,將度為1的節點不斷從無向圖中移除,直至剩餘節點數小於等於2,那麼剩餘的節點作為根節點形成的樹的高度最小。

為什麼不斷將度為1的節點從無向圖中移除最後剩下的節點就是最終結果?

這有點像貪心演算法,假設有一根竹竿,我們每次將兩頭的竹節去掉,反覆如此,最終剩下的竹節一定是位於中間的竹節,以中間的竹節為起點將竹竿折斷形成的竹竿一定是最短的,這和這道題一樣的道理。

#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<list>
#include<set>
#include<queue>
using namespace std;
class Solution {
public:
    map<int,vector<int>> Map;  //記錄樹節點的有向連線情況
    int depth;  //記錄以當前節點為根節點的樹的最大高度
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        //基本思想:最正常的思維然而超時,遍歷0~n-1每個節點,以該節點為根節點,不斷遞迴計算以此節點為根節點的樹的最大高度depth,
        //如果depth小於mindepth,說明以此節點為根節點的樹的高度是目前樹高度中最小的,更新mindepth,儲存此節點到res
        vector<int> res;
        int mindepth=1e9;
        //將邊轉化為節點有向連線情況儲存至Map
        for(int i=0;i<edges.size();i++)
        {
            Map[edges[i][0]].push_back(edges[i][1]);
            Map[edges[i][1]].push_back(edges[i][0]);
        }
        //遍歷0~n-1每個節點,以該節點為根節點,不斷遞迴計算以此節點為根節點的樹的最大高度depth
        for(int i=0;i<n;i++)
        {
            vector<int> path{i};
            depth=0;
            int h=1;
            Recursion(i,path,h);
            //如果depth小於mindepth,說明以此節點為根節點的樹的高度是目前樹高度中最小的,更新mindepth,儲存此節點到res
            if(depth<mindepth)
            {
                res.clear();
                res.push_back(i);
                mindepth=depth;
            }
            else if(depth==mindepth)
                res.push_back(i);
        }
        return res;
    }
    void Recursion(int n,vector<int> path,int h)
    {
        if(h>depth)
            depth=h;
        vector<int> nextnode=Map[n];
        for(int i=0;i<nextnode.size();i++)
        {
            vector<int>::iterator iter=find(path.begin(),path.end(),nextnode[i]);
            if(iter==path.end())
            {
                path.push_back(nextnode[i]);
                Recursion(nextnode[i],path,h+1);
            }
        }
    }
};
class Solution1 {
public:
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        //基本思想:拓撲排序思路,將度為1的節點不斷從無向圖中移除,直至剩餘節點數小於等於2,那麼剩餘的節點作為根節點形成的樹的高度最小
        //為什麼不斷將度為1的節點從無向圖中移除最後剩下的節點就是最終結果?
        //這有點像貪心演算法,假設有一根竹竿,我們每次將兩頭的竹節去掉,反覆如此,最終剩下的竹節一定是位於中間的竹節,
        //以中間的竹節為起點將竹竿折斷形成的竹竿一定是最短的,這和這道題一樣的道理。
        if(n==1)  return {0};
        vector<int> res;  //最後返回的結果
        map<int,set<int>> Map;  //記錄樹節點的有向連線關係:Map[0]={1,2,3}表示0節點指向1,2,3節點
        for(int i=0;i<edges.size();i++)
        {
            Map[edges[i][0]].insert(edges[i][1]);
            Map[edges[i][1]].insert(edges[i][0]);
        }
        queue<map<int,set<int>>::iterator> delnode;  //記錄度為1的節點的迭代器
        //將度為1的節點的迭代器儲存至delnode
        for(auto iter=Map.begin();iter!=Map.end();iter++)
        {
            if(iter->second.size()==1)
                delnode.push(iter);
        }
        //將度為1的節點不斷從無向圖中移除,直至剩餘節點數小於等於2結束迴圈
        while(Map.size()>2)
        {
            //將度為1的節點刪除,刪除之前也要將指向它的邊去除,比如刪除節點2,而Map[3]={1,2,4}那麼要將Map[3]中的節點2去除
            int queuelen=delnode.size();
            for(int i=0;i<queuelen;i++)
            {
                map<int,set<int>>::iterator iter=delnode.front();
                delnode.pop();
                //度為1的節點刪除之前將指向它的邊也去除
                set<int> curset=Map[*(iter->second.begin())];
                set<int>::iterator pos = curset.find(iter->first);
                curset.erase(pos);
                Map[*(iter->second.begin())]=curset;
                //將度為1的節點的迭代器儲存至delnode
                if(curset.size()==1)
                {
                    map<int,set<int>>::iterator curiter=Map.find(*(iter->second.begin()));
                    delnode.push(curiter);
                }
                //將度為1的節點刪除
                Map.erase(iter);
            }
        }
        for(auto iter=Map.begin();iter!=Map.end();iter++)
            res.push_back(iter->first);
        return res;
    }
};
int main()
{
    Solution1 solute;
    int n=6;
    vector<vector<int>> edges={{0,1},{0,2},{0,3},{3,4},{4,5}};
    vector<int> res=solute.findMinHeightTrees(n,edges);
    for_each(res.begin(),res.end(),[](const int &v){cout<<v<<" ";});
    cout<<endl;
}