
这个题需要复杂的证明,这里不再用数学证明。
最小高度树的高度公式
设树中距离最远的两个节点为 x, y它们之间的距离为
maxdist = dist[x][y]。则任意最小高度树的高度为
minheight=⌈maxdist/2⌉
换句话说,最小高度树的高度是最长路径长度的一半向上取整。
最小高度树的根节点位置
根节点一定在这条最长路径上。
如果根不在最长路径上,则无论怎么选,高度都不可能小于
minheight,会和最长路径长度矛盾。
只是说一说直觉就可以理解的。
想象最长路径是一条线,根放在中间,两边叶子到根的距离最均衡,形成最小高度。
如果根偏离这条路径,则最长的一边会更长,高度反而变大。
树的高度取决于最远的叶子对。
把根放在最远叶子对的中间,让两边尽量平衡。
就像把跷跷板的支点放在中间,重量(距离)最均衡,高度最小。
因此我们只需要求出路径最长的两个叶子节点即可,并求出其路径的最中间的节点即为最小高度树的根节点。可以利用以下算法找到图中距离最远的两个节点与它们之间的路径:
以任意节点 p 出现,利用广度优先搜索或者深度优先搜索找到以 p 为起点的最长路径的终点 x(树没有环,所以从任意节点出发,沿着最长的分支走,最远的点一定落在最长路径的某个端点上。);以节点 x 出发,找到以 x 为起点的最长路径的终点 y;x 到 y 之间的路径即为图中的最长路径,找到路径的中间节点即为根节点。
有了以上前置知识,我们使用拓扑排序的方法进行求解,不再使用深度搜素和广度搜索的方法:
cppclass Solution { public: vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) { if(n==1) return {0}; //创建邻接矩阵 vector<unordered_set<int>> graphs(n); for(auto& e:edges){ graphs[e[0]].insert(e[1]); graphs[e[1]].insert(e[0]); } vector<int> leves; //找叶子节点 for(int i =0;i<n;i++){ if(graphs[i].size()==1) leves.push_back(i); } // 删结点 int remaining=n; while(remaining>2){ remaining-=leves.size(); vector<int> newleves; //删边 for(auto e:leves){ int ajx=*(graphs[e].begin()); graphs[ajx].erase(e); if(graphs[ajx].size()==1) newleves.push_back(ajx); } leves=newleves; } return leves; } };思路总结
树的最小高度树的根一定在最长路径的中间 → 对应最终剩下 1 或 2 个节点。
用 剥叶子法:
反复删除所有叶子节点(度 = 1)
剩下的节点就是最小高度树的根
时间复杂度 O(n),空间复杂度 O(n)。