思路
- 首先,我们看了样例,发现这个树并不是二叉树,是多叉树。
- 然后,我们可能想到的解法是:根据题目的意思,就挨个节点遍历bfs,统计下每个节点的高度,然后用map存储起来,后面查询这个高度的集合里最小的就可以了。
- 但是这样会超时的。
- 于是我们看图(题目介绍里面的图)分析一下,发现,越是靠里面的节点越有可能是最小高度树。
- 所以,我们可以这样想,我们可以倒着来。
- 我们从边缘开始,先找到所有出度为1的节点,然后把所有出度为1的节点进队列,然后不断地bfs,最后找到的就是两边同时向中间靠近的节点,那么这个中间节点就相当于把整个距离二分了,那么它当然就是到两边距离最小的点啦,也就是到其他叶子节点最近的节点了。
- 然后,就可以写代码了。
其实实质就是找图的中心结点:
代码实现(java)
class Solution {
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer> res = new ArrayList<Integer>(); // 存放结果
// 如果这个图只有一个结点,那么它自己就是最小高度树
if (n == 1) {
res.add(0);
return res;
}
// 建立表示各个结点度的表
int[] degree = new int[n];
// 建立图关系,表示各个节点的相连关系(邻居)
HashMap<Integer, ArrayList<Integer>> map = new HashMap<>();
// 遍历给定的图关系,初始化结点度表 和 邻居关系
for (int[] edge : edges) {
int a = edge[0];
int b = edge[1];
// 这时候 结点a和b都各有自己的一个度(相对于对方来说:a的度到b,b的度到a)
degree[a]++;
degree[b]++;
// 如果此时邻居表中还没有a或者b,则初始化邻居表
if (map.get(a) == null) {
map.put(a, new ArrayList<Integer>());
}
if (map.get(b) == null) {
map.put(b, new ArrayList<Integer>());
}
// 为a和b添加邻居
map.get(a).add(b);
map.get(b).add(a);
}
// 建立队列
Queue<Integer> queue = new LinkedList<Integer>();
// 将所有度为1的结点加入队列
for (int i = 0; i < n; i++) {
if (degree[i] == 1) {
queue.offer(i);
}
}
// 循环判断删除每层的叶子节点
while (!queue.isEmpty()) {
res = new ArrayList<>(); // 每层创建新的集合,最终保存的就是最小高度树了
int size = queue.size();
for (int i = 0; i < size; i++) {
int cur = queue.poll();
// 把当前结点加入结果集,因为每次循环都会新创建集合然后覆盖掉上层的集合
// 也就是说只要还能循环,就说明没有到达最内层,到达最内层则保存的就是最小高度树的根了
res.add(cur);
// 将当前结点的邻居集合,然后判断邻居是否为下一层的叶子节点,是就加入队列
ArrayList<Integer> neighbors = map.get(cur);
for (int neighbor: neighbors) {
degree[neighbor]--; // 相当于删除当前叶子节点,邻居的度减一
if (degree[neighbor] == 1) {
queue.offer(neighbor);
}
}
}
}
return res;
}
}