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]]

  1. 0
  2. |
  3. 1
  4. / \
  5. 2 3

return [1]

Example 2:

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

  1. 0 1 2
  2. \ | /
  3. 3
  4. |
  5. 4
  6. |
  7. 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 = ;
  14. for (int i = ; 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) != ){
  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 + ;
  33. }
  34.  
  35. vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
  36.  
  37. map<int, gnode*> val_node;
  38. for(int i = ; i < n ; i++){
  39. gnode* node = new gnode(i);
  40. val_node[i] = node;
  41. }
  42.  
  43. for (int i = ; 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 = ; i < n ; i++){
  14. TNode* tmp = new TNode(i);
  15. val_node[i] = tmp;
  16. }
  17.  
  18. for (int i = ; 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() > ){
  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() == ){
  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. }

参考资料:

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

[LeetCode] 310. Minimum Height Trees 解题思路的更多相关文章

  1. 【LeetCode】310. Minimum Height Trees 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS 相似题目 参考资料 日期 题目地址:http ...

  2. [LeetCode] 310. Minimum Height Trees 最小高度树

    For a undirected graph with tree characteristics, we can choose any node as the root. The result gra ...

  3. leetcode@ [310] Minimum Height Trees

    For a undirected graph with tree characteristics, we can choose any node as the root. The result gra ...

  4. [LeetCode] 310. Minimum Height Trees_Medium tag: BFS

    For a undirected graph with tree characteristics, we can choose any node as the root. The result gra ...

  5. 310. Minimum Height Trees

    For a undirected graph with tree characteristics, we can choose any node as the root. The result gra ...

  6. 310. Minimum Height Trees -- 找出无向图中以哪些节点为根,树的深度最小

    For a undirected graph with tree characteristics, we can choose any node as the root. The result gra ...

  7. [LeetCode] 76. Minimum Window Substring 解题思路

    Given a string S and a string T, find the minimum window in S which will contain all the characters ...

  8. [LeetCode] Minimum Height Trees 最小高度树

    For a undirected graph with tree characteristics, we can choose any node as the root. The result gra ...

  9. LeetCode Minimum Height Trees

    原题链接在这里:https://leetcode.com/problems/minimum-height-trees/ 题目: For a undirected graph with tree cha ...

随机推荐

  1. Android Studio 快捷键(转)

    Android Studio 快捷键 操作 Mac OSX Win/Linux 注释代码(//) Cmd + / Ctrl + / 注释代码(/**/) Cmd + Option + / Ctrl + ...

  2. Android中使用HttpGet和HttpPost访问HTTP资源

    需求:用户登录(name:用户名,pwd:密码) (一)HttpGet :doGet()方法//doGet():将参数的键值对附加在url后面来传递 public String getResultFo ...

  3. POJ 2049 Finding Nemo

    Finding Nemo Time Limit: 2000MS   Memory Limit: 30000K Total Submissions: 8631   Accepted: 2019 Desc ...

  4. SPOJ 181 - Scuba diver 二维背包

    潜水员要潜水,给出n个气缸(1<=n<=1000),每个气缸中有氧气量为ti,氮气量为ai,气缸重量为wi(1<=ti<=21,1<=ai<=79,1<=wi ...

  5. Activity 【生命周期】

    不同情况下的回调 我们打开应用时先后调用了onCreate()->onStart()->onResume 当我们按BACK键时,我们这个应用程序将结束,这时候我们将先后调用onPause( ...

  6. OD: RPC - MS06040 & MS08067

    RPC 漏洞简介 Remote Procedure Call,分布式计算中常用到的技术.两台计算机通信过程可以分为两种形式:一种是数据的交换,另一种是进程间通信.RPC 属于进程间通信. RPC 就是 ...

  7. 3xian之所在

    最后一天,漫天飘起了雪花,假装欢送我离去. 这次WF之战不太顺利,早期的C题大概花了1秒钟构思,然而由于输出格式多了一个空格直到两个半小时才逃脱Wrong Answer的纠缠.还好lynncui在期间 ...

  8. 解决MVC Json序列化的循环引用问题/EF Json序列化循引用问题---Newtonsoft.Json

    1..Net开源Json序列化工具Newtonsoft.Json中提供了解决序列化的循环引用问题: 方式1:指定Json序列化配置为 ReferenceLoopHandling.Ignore 方式2: ...

  9. 关于android MTK相机L版本,切换屏幕比例后,分辨率随之改变,但重新进入相机后原有分辨率不再生效问题

    BUG详细:比如4:3的时候是200W,切成全屏变400W,重新切回4:3为300W,退出相机后,重新进入又变成200W. 原因分析:这个版本的设计如此,当你点选屏幕比例的时候,程序设计是把这个比例值 ...

  10. 你好,C++(31)我终于找到对象啦!6.1 从结构化设计到面向对象程序设计

    第6章 当C++爱上面向对象 很多第一次进入C++世界的人都会问:C++中的那两个加号到底是什么意思啊? C++是由C语言发展而来的,它比C语言多出的两个加号,实际上是C语言的自增操作符,表示C++语 ...