For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels.
Format
The graph contains n
nodes which are labeled from 0
to n - 1
. You will be given the number n
and a list of undirected edges
(each edge is a pair of labels).
You can assume that no duplicate edges will appear in edges
. Since all edges are undirected, [0, 1]
is the same as [1, 0]
and thus will not appear together in edges
.
Example 1:
Given n = 4
, edges = [[1, 0], [1, 2], [1, 3]]
0 | 1 / \ 2 3
return [1]
Example 2:
Given n = 6
, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
0 1 2 \ | / 3 | 4 | 5
return [3, 4]
Hint:
- How many MHTs can a graph have at most?
Note:
(1) According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.”
(2) The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.
問題:給定一個擁有樹性質的無向圖,圖的每一個節點都可以視為一棵樹的根節點。在所有可能的樹中,找出高度最小的樹,並返回他們的樹根。
思路一 :
求最小高度的樹,實際上是一個求最優解題目。求最優解,我首先想到的是動態規划(DP)思路。這個題目也確實滿足 DP 的兩個主要性質:overlapping substructure & optimal substructure 。思路比較直觀:
- 視某個節點為樹根節點,求這棵樹的樹高。
- 對所有節點進行上面的求解,得到 n 個樹高,其中高度最小的樹的樹根即為原題目的解。
對於1,如何求解節點 A 為樹根的樹高?
假設已知 A 的所有相鄰節點分別為樹根的各個子樹的樹高,那么 A根的樹高等於 已知的各個子樹樹高中的最大值 加一。方程式表達如下,即狀態轉換方程:
height(A) = max(height(A.next0), height(A.next2),... height(A.nextk)) + 1
對於2, 存在大量重復計算。可以借助表格,將已計算的樹分支高度保存下來后續使用,避免重復計算。將當前節點以及其中一個相鄰節點組合分支方向,求得該分支高度后存入 map<string, int> direc_height 中,其中 direc 有這兩個節點組成作為 key ,height 表示高度。
若不使用表格優化,時間復雜度為 O(V*E)。使用表格,相當於進行了剪枝,會快不少。跑 LeetCode 的大集合 V = 1000, E =2000 ,測試耗時是 820ms,可惜沒能過 submit 要求。

1 private: 2 3 // dirct consists of two node cur_next, height means the height of the subtree which souce node is the current node and walk forward on the cur_next direction 4 map<string, int> direc_height; 5 6 public: 7 8 /** 9 * calulate the height of a tree which parent node is p and current node is node 10 * 11 */ 12 int getHeight(gnode* node, gnode* p){ 13 int h = 0; 14 for (int i = 0; i < node->neighbours.size(); i++){ 15 gnode* tmpn = node->neighbours[i]; 16 if (tmpn == p){ 17 continue; 18 } 19 20 int tmph; 21 string str = to_string(node->val) + "_" + to_string(tmpn->val); 22 if(direc_height.count(str) != 0){ 23 tmph = direc_height[str]; 24 }else{ 25 tmph = getHeight(tmpn, node); 26 direc_height[str] = tmph; 27 } 28 29 h = max(h, tmph); 30 } 31 32 return h + 1; 33 } 34 35 vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) { 36 37 map<int, gnode*> val_node; 38 for(int i = 0; i < n ; i++){ 39 gnode* node = new gnode(i); 40 val_node[i] = node; 41 } 42 43 for (int i = 0; i < edges.size(); i++){ 44 pair<int, int> pp = edges[i]; 45 val_node[pp.first]->neighbours.push_back(val_node[pp.second]); 46 val_node[pp.second]->neighbours.push_back(val_node[pp.first]); 47 } 48 49 int minh = INT_MAX; 50 51 map<int, int> node_height; 52 53 map<int, gnode*>::iterator m_iter; 54 for(m_iter = val_node.begin(); m_iter != val_node.end(); m_iter++){ 55 int h = getHeight(m_iter->second, NULL); 56 node_height[m_iter->first] = h; 57 minh = min(minh, h); 58 } 59 60 vector<int> res; 61 map<int, int>::iterator mii_iter; 62 for(mii_iter = node_height.begin(); mii_iter != node_height.end(); mii_iter++){ 63 if(mii_iter->second == minh){ 64 res.push_back(mii_iter->first); 65 } 66 } 67 68 return res; 69 }
思路二 :
除了 DP 方案,沒有想到其他思路,在網上借鑒了其他了的想法,理解后實現通過。
這個思路實際上是一個 BFS 思路。和常見的從根節點進行 BFS 不同,這里從葉子節點開始進行 BFS。
所有入度(即相連邊數)為 1 的節點即是葉子節點。找高度最小的節點,即找離所有葉子節點最遠的節點,也即找最中心的節點。
找最中心的節點的思路很簡單:
- 每次去掉當前圖的所有葉子節點,重復此操作直到只剩下最后的根。
根據這個思路可以回答題目中的 [ hint : How many MHTs can a graph have at most? ],只能有一個或者兩個最小高度樹樹根。證明省略。

1 class TNode{ 2 public: 3 int val; 4 unordered_set<TNode*> neighbours; 5 TNode(int val){ 6 this->val = val; 7 } 8 }; 9 10 vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) { 11 map<int, TNode*> val_node; 12 13 for (int i = 0 ; i < n ; i++){ 14 TNode* tmp = new TNode(i); 15 val_node[i] = tmp; 16 } 17 18 for (int i = 0 ; i < edges.size(); i++){ 19 pair<int, int> pp = edges[i]; 20 val_node[pp.first]->neighbours.insert(val_node[pp.second]); 21 val_node[pp.second]->neighbours.insert(val_node[pp.first]); 22 } 23 24 map<int, TNode*>::iterator m_iter; 25 26 while(val_node.size() > 2){ 27 28 // obtain all leaves in current graph; 29 list<TNode*> listg; 30 for ( m_iter = val_node.begin(); m_iter != val_node.end(); m_iter++){ 31 if(m_iter->second->neighbours.size() == 1){ 32 listg.push_back(m_iter->second); 33 } 34 } 35 36 // remove all leaves 37 list<TNode*>::iterator l_iter; 38 for(l_iter = listg.begin(); l_iter != listg.end(); l_iter++){ 39 TNode* p = (*(*l_iter)->neighbours.begin()); 40 p->neighbours.erase(*l_iter); 41 (*l_iter)->neighbours.erase(p); 42 43 val_node.erase((*l_iter)->val); 44 } 45 } 46 47 vector<int> res; 48 for ( m_iter = val_node.begin(); m_iter != val_node.end(); m_iter++){ 49 res.push_back(m_iter->first); 50 51 } 52 53 return res; 54 }
參考資料: