310最小高度樹(拓撲排序)
阿新 • • 發佈:2020-12-19
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; }