【2025大数据秋招】每日一题-最小高度树

声明:

本篇算法解析具体来源是评论区的具体题解,在这里只是对代码过程的自我感悟。
具体链接:算法链接

题目:

树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。

给你一棵包含 n 个节点的树,标记为 0 0 0 n − 1 n - 1 n1 。给定数字 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之后一切都清晰起来。

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值