• [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 

  • 相关阅读:
    web前端知识体系大全【欢迎补充】
    ESA2GJK1DH1K基础篇: 阿里云物联网平台: 云平台显示单片机采集的温湿度数据,控制设备继电器(基于ESP8266,AT指令TCP_MQTT通信)(V0.1)
    ESA2GJK1DH1K基础篇: 阿里云物联网平台: 使用阿里云物联网平台提供的物理模型Topic通信控制(ESP8266,TCP透传指令)
    ESA2GJK1DH1K基础篇: 阿里云物联网平台: 使用阿里云物联网平台提供的自定义Topic通信控制(ESP8266,TCP透传指令)
    ESA2GJK1DH1K基础篇: 关于各大物联网平台的MQTT通信
    ESA2GJK1DH1K微信小程序篇: 小程序连接MQTT简单的Demo
    Android 开发基础入门: Android Studio 导入工程
    Android 开发基础入门: android studio安装教程
    ESA2GJK1DH1K升级篇: 移植远程更新程序到STM32F103RET6型号的单片机,基于(GPRS模块AT指令TCP透传方式)
    【传输协议】http协议GET与POST传递数据的最大长度能够达到多少
  • 原文地址:https://www.cnblogs.com/TonyYPZhang/p/5123058.html
Copyright © 2020-2023  润新知