题目
树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。
给你一棵包含 n 个节点的树,标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表(每一个边都是一对标签),其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条无向边。
可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树 。
请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。
树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。
提示:
1 <= n <= 2 * 10^4
edges.length == n - 1
0 <= ai, bi < n
ai != bi
所有 (ai, bi) 互不相同
给定的输入 保证 是一棵树,并且 不会有重复的边
示例 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]
思路
这题看起来很简单,不就是求最小高度树嘛,依次求每个节点高度不就完了,bfs一顿写后,结果发现时间复杂度超了…
n的范围在那摆着,n2级的时间复杂度肯定玩完。
那怎么能缩小时间复杂度呢?
首先我们应该明确的一点是:想要计算树的高度只有bfs和dfs两种方式
但是计算每个节点的高度肯定超时,那么我们可不可以寻找一种方式,不需要计算高度呢。
使劲看案例中的图像,首先可以一点:只有一条边的节点做节点时一定不是最短高度树(n>2),这样就可以说他是一个叶子节点,是一个边界。
借此思路,我们沿着边界的点往里收缩,可以发现中间的节点好像离其他所有点都比较近。
这就可以判定从边为1的点开始往图中心收缩,最中间的点作为根的树一定是最小生成树。
怎么收缩?从叶子节点开始往里层次遍历(bfs)
需要建立数据结构:
- ans:保存当前最小距离节点
- umap:邻接表,记录每个点的邻接点
- E:记录每个点的度(相连边的数量)
- q:队列,用于bfs
具体流程如下:
- 将所有节点关系存储在邻接表umap中,度存储在E中
- 将度为1的节点入队
- 进行层次遍历:直到寻找到最中间的点
- 输出
代码
class Solution {
public:
vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
if(n==1) return {0};
vector<int> ans;
unordered_map<int, vector<int>> umap;
vector<int> E(n);
for(auto e:edges){
E[e[0]]++;
E[e[1]]++;
umap[e[0]].push_back(e[1]);
umap[e[1]].push_back(e[0]);
}
queue<int> q;
for(int i=0;i<n;i++){
if(E[i]==1)
q.push(i);
}
//层次遍历
while(!q.empty()){
ans.clear();
int size = q.size();
//遍历当前层节点
for(int i=0;i<size;i++){
int t = q.front();
q.pop();
ans.push_back(t); //暂且将该层节点当作最中间的节点
//遍历该节点的邻居节点
for(auto x:umap[t]){
E[x]--; //意思是t与x相连的边可以不用考虑了,因为上面用过了
if(E[x]==1)
q.push(x); //入队
}
}
}
return ans;
}
};