树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,任何一个没有简单环路的连通图都是一棵树。
给你一棵包含 n
个节点的树,标记为 0
到 n - 1
。给定数字 n
和一个有 n - 1
条无向边的 edges
列表(每一个边都是一对标签),其中 edges[i] = [ai, bi]
表示树中节点 ai
和 bi
之间存在一条无向边。
可选择树中任何一个节点作为根。当选择节点 x
作为根节点时,设结果树的高度为 h
。在所有可能的树中,具有最小高度的树(即,min(h)
)被称为 最小高度树 。
请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。
树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。
示例 1:
输入:n = 4, edges = [[1,0],[1,2],[1,3]] 输出:[1] 解释:如图所示,当根是标签为 1 的节点时,树的高度是 1 ,这是唯一的最小高度树。
示例 2:
输入:n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]] 输出:[3,4]
提示:
1 <= n <= 2 * 10^4
edges.length == n - 1
0 <= ai, bi < n
ai != bi
- 所有
(ai, bi)
互不相同 - 给定的输入 保证 是一棵树,并且 不会有重复的边
思路:拓扑排序的变形。类似剥洋葱的方法,就是一层一层的褪去叶节点,最后剩下的一个或两个节点就是我们要求的最小高度树的根节点。
1、采用邻接表来表示无向图,抽象成:
(1)一个二维数组,里面每一个一维数组存以该索引为顶点的邻接顶点。
(2)一个一维数组 cnt, 来表示每个顶点的邻接顶点数目。
2、我们开始先根据输入来建立这个有向图,并将cnt数组也初始化好。
3、设置一个queue:
(1)将所有邻接顶点数目为1的点压队列。
(2)将队首元素输出,并把该顶点的所有连接边删去,也就是把它的各个邻接顶点的邻接顶点数目-1。
(3)将新的邻接顶点数目为1的顶点再入队列。
(4)重复过程(2)-(3),直到最后剩下的一个或两个节点就是我们要求的最小高度树的根节点。
可以维护一个初始化为n的变量来统计数目。注意一次性要删掉这一批所有的叶子结点的数目。直到n<=2为止。
class Solution {
public:
vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
vector<vector<int> >g(n);//每一个一维数组存以该索引为顶点的邻接顶点
vector<int>cnt(n);//每个顶点的邻接顶点数目
if(n==1) return {0};
int c=n;
for(auto x: edges){
g[x[1]].push_back(x[0]);
g[x[0]].push_back(x[1]);
cnt[x[0]]++;
cnt[x[1]]++;
}
queue<int>q;
for(int i=0; i<cnt.size(); ++i){
if(cnt[i]==1){
q.push(i);
}
}
while(c>2){
int len=q.size();
c-=len;
for(int i=0; i<len; ++i){
int k=q.front();
q.pop();
for(auto x: g[k]){
if(--cnt[x]==1){
q.push(x);
}
}
}
}
vector<int>re;
while(!q.empty()){
re.push_back(q.front());
q.pop();
}
return re;
}
};