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 }
参考资料: