[LeetCode] 310. Minimum Height Trees 解題思路


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 = 4edges = [[1, 0], [1, 2], [1, 3]]

        0
        |
        1
       / \
      2   3

return [1]

Example 2:

Given n = 6edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]

     0  1  2
      \ | /
        3
        |
        4
        |
        5

return [3, 4]

Hint:

  1. 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 。思路比較直觀:

  1. 視某個節點為樹根節點,求這棵樹的樹高。
  2. 對所有節點進行上面的求解,得到 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     }
View Code

 

思路二 : 

除了 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     }
View Code

 

參考資料:

C++ Solution. O(n)-Time, O(n)-Space, LeetCode OJ 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM