LeetCode-310.最小高度树 基于广度优先搜索解决拓扑排序问题思路

这里是题目描述:LeetCode-310.最小高度树

想要找到由给定的图构成的最小高度树的根节点,我们可以删除图中 “最外围” 的节点;删除操作后,又有一部分之前不处于 “最外围” 的节点成为新的“最外围节点”;因此不断重复执行 “删除最外围节点” 这一操作,直到只剩下位于原图 “最中心” 位置的节点,它们就是我们所寻找的最小高度树的根节点

那么,如何循环地确定并删除最外围的节点呢?我们借助解决拓扑排序问题的思路,当然也有所不同,先说明我们的思路:

统计给定图中所有节点的“度”;将当前度为1的节点,就是最外围的节点存入一个队列;将队列中的节点出队列,将它们从图中删除并将与它们相连的还未进队列的节点的“度”减1;队列中的节点删除完毕后,再次将图中新的“度为1”也就是新的最外围的节点从图中删除并入队列,如此循环操作,直到,只剩下一个或多个“最中心”的节点最晚入队列,它们就是我们所寻找的最小高度树根节点。注意,我们每次需要一次性将当前的所有在队列中的度为1的节点出队列并删除,然后再重新将经过“度减法”成为新的最外围的元素统一入队列;而不是在出队列的同时随时就将新的最外围节点入队列,因为这样将导致无法确定最中心节点

在这里插入图片描述
我们在解决本题时借助了解决拓扑排序问题的思路,并借助队列来存储“最外围节点”,并在出队列的同时不断确定下一批进队列的节点,因此也可以说借助了官渡有限搜索的思想。但是我们问题及解法依然和拓扑排序问题及解法有一些不同点:

  1. 本题给定的是一个无向无环图,拓扑排序给的是有向无环图
  2. 我们统计“度”为1的节点进队列,并将它们一次性出队列并删除,然后再次统计新的“度”为1的点进队列;拓扑排序统计“入度”为0的点进队列,并且在出队列的过程中可以随时将新的“入度”为0的点进队列,不要求一次性清空队列
  3. 本题图中最终剩下“位于最中心”位置的节点;拓扑排序最终剩下“最尽头”位置的点

题解代码:

class Solution {
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        if(n<=1)
        {
            List<Integer> list=new ArrayList<>();
            list.add(n-1);
            return list;
        }
        boolean[] inQueue=new boolean[n]; //记录一个节点是否已经进队列
        int numIn=0; //记录已经进队列的节点个数
        boolean[][] graph=new boolean[n][n]; //用二位矩阵表示图
        int[] degree=new int[n]; //记录各个节点的度
        for(int i=0;i<edges.length;i++)
        {
            graph[edges[i][0]][edges[i][1]]=true;
            graph[edges[i][1]][edges[i][0]]=true;
            degree[edges[i][0]]+=1;
            degree[edges[i][1]]+=1;
        }
        Queue<Integer> queue=new LinkedList<>(); //让度为1的节点进队列
        for(int i=0;i<degree.length;i++)
        {
            if(degree[i]==1)
            {
                queue.offer(i);
                inQueue[i]=true;
                numIn+=1;
            }
        }
        while(numIn<n)
        {
            Queue<Integer> tempQueue=new LinkedList<>();
            while(!queue.isEmpty()) //一次性把队列中的所有节点出队列
            {
                int o=queue.remove();
                for(int i=0;i<graph[o].length;i++)
                {
                    if(graph[o][i] && !inQueue[i])
                    {
                        degree[i]-=1; //相应节点的度减1
                        if(degree[i]<=1) //度被减为1时,进入临时队列
                        {
                            tempQueue.offer(i);
                            inQueue[i]=true;
                            numIn+=1;
                        }
                    }
                }
            }
            queue=tempQueue; //临时队列中的节点进入队列,进行下一轮出队列
        }
        List<Integer> res=new ArrayList<>();
        while(!queue.isEmpty()) //最后一轮队列中的度为1的节点就是所求结果
        {
            res.add(queue.remove());
        }
        return res;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值