声明:
本篇算法解析具体来源是评论区的具体题解,在这里只是对代码过程的自我感悟。
具体链接:算法链接
题目:
树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。
给你一棵包含 n 个节点的树,标记为 0 0 0 到 n − 1 n - 1 n−1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表(每一个边都是一对标签),其中 e d g e s [ i ] edges[i] edges[i] = [ a i {a_i} ai, b i {b_i} bi] 表示树中节点 a i {a_i} ai 和 b i {b_i} bi之间存在一条无向边。
可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树 。
请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。树的高度是指根节点和叶子节点之间最长向下路径上边的数量。
输入:
n = 4, edges = [[1,0],[1,2],[1,3]]
输出:
[1]
解释:
如图所示,当根是标签为 1 的节点时,树的高度是 1 ,这是唯一的最小高度树。
思路:
1、阅读本题,我们不难能够联想到使用DFS或者BFS来进行遍历,最终找到最小高度的树。多叉树我们可以看作一个图的结构,【最矮树即使是裁剪了叶子节点之后,仍然是最矮树】。
2、队列最后剩下的元素,就是我们求得的最矮树的根节点,我们将树的叶子节点(度为1的节点)放入至队列当中,通过pop删除度的方式,最后找到的就是两边同时向中间靠近的节点,那么这个中间节点就相当于把整个距离二分了,那么它当然就是到两边距离最小的点。
代码:
这样说其实还是有点抽象,我们根据具体代码进行解析:
现在我们测试的数据是:
int n = 4;
int[][] edges = {{1,0}, {1,2}, {1,3}};
1、首先我们需要声明具体的结果集res,同时我们需要一个数组degree来记录树中节点的度变化情况,通过一个list来建立图关系。
List<Integer> res = new ArrayList<>();
/*如果只有一个节点,那么他就是最小高度树*/
if (n == 1) {
res.add(0);
return res;
}
/*建立各个节点的出度表*/
int[] degree = new int[n];
/*建立图关系,在每个节点的list中存储相连节点*/
List<List<Integer>> map = new ArrayList<>();
for (int i = 0; i < n; i++) {
map.add(new ArrayList<>());
}
for (int[] edge : edges) {
degree[edge[0]]++;
degree[edge[1]]++;/*出度++*/
map.get(edge[0]).add(edge[1]);/*添加相邻节点*/
map.get(edge[1]).add(edge[0]);
}
这里我们得到的结果是:
其中0表示的是value为0的节点的度,以此类推。在Map当中我们可以看到以<key, value>格式的边
在key为0的,对应存储的是<0,1>,因为1的度为3,所以我们可以得到3个键值对,分别是<1,0>, <1,2>, <1,3> 以此类推。
2、在此基础上,我们需要声明一个队列,我们将所有出度为1的节点加入至队列当中:
Queue<Integer> que = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (degree[i] == 1) queue.offer(i);
}
3、我们遍历队列当中的元素,通过循环new一个结果集合,最后我们得到的就是最矮树的根节点
while (!queue.isEmpty()) {
res = new ArrayList<>();
int size = queue.size();/*这是每一层的节点的数量*/
for (int i = 0; i < size; i++) {
int cur = queue.poll();
res.add(cur);
List<Integer> neighbors = map.get(cur);
for (int neighbor : neighbors) {
degree[neighbor]--;
if (degree[neighbor] == 1) {
/*如果是叶子节点我们就入队*/
queue.offer(neighbor);
}
}
}
}
通过我们上述例子中,我们第一轮的循环入队的分别是0,2,3。那么,对应的循环遍历中,遍历0元素时,因为0的连接的邻居是1,从map中拿到的结果是1。
那么与此同时degree[1]–; 也实现了我们删减叶子节点的操作,所以1节点的度减少1.
当我们遍历完队列中的元素的时候,degree[1]此时变成1,那么我们再将度为1的1节点加入,在1节点入队的时候,1已经没有邻居了,所以此时运行到最后的时候队列是空的,最后我们就脱离循环。
总结:
我们可以使用List<List<>>来构建一个图,并且可以通过队列删除叶子节点的方式得到多叉树的最矮高度。虽然过程有点抽象,但是经过debug之后一切都清晰起来。