树与图中的dfs和bfs—— AcWing 846. 树的重心 AcWing 847. 图中点的层次

简介: 树与图中的dfs和bfs—— AcWing 846. 树的重心 AcWing 847. 图中点的层次

一、AcWing 846. 树的重心


1.1题目



1.2思路分析


题意:什么是树的重心?


树的重心是指,删除某个结点后剩下的最大连通子树的结点数目最小,如下图是根据样列生成的树,若删除结点1,则剩下三个子树最大的是中间那颗结点有4个,即剩下的最大连通子树的结点数目为4;若删除结点2,则剩下两个数目为1的子树和一个数目为6的子树,即剩下的最大连通子树的结点数目为6;若删除结点3,剩下一个数目为1的子树,和一个数目为7的子树,即剩下的最大连通子树的结点数目为7……枚举可得剩下的最小的最大连通子树的结点数目为4也就是说结点1是树的重心。另外注意题目要求答案是输出剩下的最小的最大连通子树的结点数目。


思路


深搜,算出每个结点被删除后剩下的最大连通子树的结点数目,输出最小值即可,那么问题就是怎么求一个结点被删除后的最大连通子树的结点数目,删除一个结点后,剩下的子树可以被分为两个部分,例如删除结点4:


蓝色部分是结点4的子树,红色部分我们暂时称为其他部门,因为我们知道树的总结点数n,只要能算出蓝色部分的数目s,那么其他部分的数目就是n-s。


1.3Ac代码


import java.io.*;
import java.util.Arrays;
public class Main {
    static int N = 100010;
    static int M=2*N; //n-1条无向边需要两倍空间来存储
    static int[] h = new int[N]; //邻接表存储树,有n个节点,所以需要n个队列头节点
    static boolean[] st = new boolean[N]; //记录节点是否被访问过,访问过则标记为true
    static int[] e = new int[M]; //存储元素
    static int[] ne = new int[M]; //存储列表的next值
    static int n, idx; //题目所给的输入,n个节点以及单链表指针
    static int ans=N;   //表示重心的所有的子树中,最大的子树的结点数目
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n=Integer.parseInt(br.readLine());
        Arrays.fill(h,1,n+1,-1);    //结点从1开始,开始时边为空,即h数组值为-1表示无出边
        for (int i = 0; i < n-1; i++) {
        String []s=br.readLine().split(" ");
        int a=Integer.parseInt(s[0]);
        int b=Integer.parseInt(s[1]);
        add(a,b);   add(b,a);
        }
        dfs(1);
        System.out.println(ans);
    }
    private static int  dfs(int u) {
        st[u]=true;
        int sum=1;  //当前子树大小,包括自己故从1开始
        int res=0;  //删除该结点后每一个连通块大小的最大值
        for(int i=h[u];i!=-1;i=ne[i]){
            int j=e[i];
            if(!st[j]){
                int s=dfs(j);    //其儿子子树的大小
                res=Math.max(res,s); //找出儿子子树中的最大值
                sum+=s;
            }
        }
        res=Math.max(res,n-sum);//每个结点dfs最终得出的这个res即为以该结点为重心,删除后各个连通块中点数的最大值
        ans=Math.min(res,ans);
        return sum;
    }
    private static void add(int a, int b) {
        e[idx]=b;
        ne[idx]=h[a];
        h[a]=idx++;
    }
}


二、AcWing 847. 图中点的层次


2.1题目




2.2思路分析


用 d数组保存1号节点到各个节点的距离。


用 st 数组标记各个节点有没有走到过。


从 1 号节点开始,广度优先遍历

1 号节点入队列,d[1] 的值更新为 0。


如果队列非空,就取出队头,找到队头节点能到的所有节点。如果队头节点能到走到的节点没有标记过,就将节点的d值更新为队头的d值+1,然后入队。


重复步骤 2 直到队列为空。


这个时候,d数组中就存储了 1 号节点到各个节点的距离了。


图的存储:邻接表


用 h 数组保存各个节点能到的第一个节点的编号。开始时,h[i] 全部为 -1。


用 e 数组保存节点编号,ne 数组保存 e 数组对应位置的下一个节点所在的索引。


用 idx 保存下一个 e 数组中,可以放入节点位置的索引


插入边使用的头插法,例如插入:a->b。首先把b节点存入e数组,e[idx] = b。然后 b 节点的后继是h[a],ne[idx] = h[a]。最后,a 的后继更新为 b 节点的编号,h[a] = idx,索引指向下一个可以存储节点的位置,idx ++ 。


2.3Ac代码


import java.io.*;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Map;
import java.util.Queue;
public class Main {
    static int N = 100010;
    static int[] h = new int[N]; //邻接表存储树,有n个节点,所以需要n个队列头节点
    static boolean[] st = new boolean[N]; //记录节点是否被访问过,访问过则标记为true
    static int[] d = new int[N]; //存储距离
    static int[] e = new int[N]; //存储元素
    static int[] ne = new int[N]; //存储列表的next值
    static int n,m, idx; //题目所给的输入,n个节点以及单链表指针
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String []s=br.readLine().split(" ");
        n=Integer.parseInt(s[0]);    m=Integer.parseInt(s[1]);
        Arrays.fill(h,-1);
        for (int i = 0; i < m; i++) {
            s=br.readLine().split(" ");
            int a=Integer.parseInt(s[0]);
            int b=Integer.parseInt(s[1]);
            add(a,b);
        }
        bfs();
        System.out.println(d[n]);
    }
    private static void bfs() {
        Queue<Integer> q=new ArrayDeque<>();
        Arrays.fill(d,-1);
        q.add(1);
        d[1]=0;
        st[1]=true;
        while (!q.isEmpty()){
         int he=q.poll();
         for(int i=h[he] ;i!=-1;i=ne[i]){
             int t=e[i];
             if(!st[t]){
                 d[t]=d[he]+1;
                 q.add(t);
                 st[t]=true;
             }
         }
        }
    }
    private static void add(int a, int b) {
        e[idx] = b;
        ne[idx] = h[a];
        h[a] = idx++;
    }
}
相关文章
|
数据挖掘 数据库
睡眠健康数据分析(上)
睡眠健康数据分析
1095 0
|
设计模式 前端开发 Java
总结丨Spring 源码学习,看这一篇就够了
在日常工作中,产品不断写业务需求,他们加班一天,我们开发就得工作一周来完成。 业务领域达到一定地步后,发现日常编写业务代码已经很难让我有突破性的进步,日复一日,担心自己变成一个业务代码生产机器,而无法面对新技术和环境变化。 同时也有危机感,长江后浪推前浪,自己不继续学习的话,很快就会有人超过。 而且我算是比较热心的好同学,喜欢帮别人解决问题和记录解决方案,所以不希望在别人问我工作中有什么常用的框架,遇到这个问题该怎么办,我却回答不上的感觉
8299 1
总结丨Spring 源码学习,看这一篇就够了
|
安全 测试技术 Shell
Metasploit框架技术浅析
Metasploit框架是广泛使用的渗透测试工具,支持漏洞利用、有效载荷执行、辅助操作等功能,适用于安全研究与测试。通过模块化设计,Metasploit提供了从信息收集到后渗透的完整攻击流程,助力提升系统安全性。使用时需遵守法律,确保测试授权。
441 70
|
消息中间件 供应链 测试技术
图解 DDD,这一篇总结太全面了!
DDD领域驱动是非常热的架构设计,微服务也有大量涉及,本文详细解析领域驱动设计(DDD),涵盖DDD原理、实践步骤及核心概念等,帮助更好地管理复杂业务逻辑。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 DDD,这一篇总结太全面了!
|
应用服务中间件 nginx Docker
docker应用部署---nginx部署的配置
这篇文章介绍了如何使用Docker部署Nginx服务器,包括搜索和拉取Nginx镜像、创建容器并设置端口映射和目录映射,以及如何创建一个测试页面并使用外部机器访问Nginx服务器。
|
移动开发 前端开发 JavaScript
使用html-to-image代替html2canvas,结合jspdf实现下载pdf(下载截图下载前端dom元素)
本文介绍了在前端项目中,当使用`html2canvas`遇到问题时,如何使用`html-to-image`库作为替代方案,结合`jspdf`实现将DOM元素生成为PDF文件并提供下载。文章首先讨论了`html2canvas`可能遇到的问题,并提供了该库的使用示例代码。随后,详细介绍了`html-to-image`库的安装和使用方法,展示了如何将DOM元素转换为Canvas,再利用`jspdf`生成PDF文件。最后,文章通过示例代码说明了整个转换和下载的过程,并展示了效果截图。
1589 0
|
机器学习/深度学习 人工智能 自然语言处理
|
传感器 数据可视化 物联网
【颠覆想象的科技盛宴】当区块链邂逅物联网与虚拟现实:一场关于透明农田、智能耕种与沉浸式体验的未来戏剧,正缓缓拉开帷幕!
【8月更文挑战第21天】探索区块链、物联网与虚拟现实技术的交汇点。这些技术融合为数据安全、物理-数字连接及沉浸式体验提供了强大支持。以智慧农业为例,通过区块链确保数据不可篡改,物联网收集环境数据,虚拟现实技术实现数据可视化,共同提升农业效率与食品透明度,展现未来智能世界的无限可能。
263 1
|
SQL 关系型数据库 MySQL
【MySQL】DQL-聚合函数介绍&常见聚合函数&语法&注意事项&可cv例题语句
【MySQL】DQL-聚合函数介绍&常见聚合函数&语法&注意事项&可cv例题语句
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~结构型]处理多维度变化——桥接模式
[设计模式Java实现附plantuml源码~结构型]处理多维度变化——桥接模式
256 0
下一篇
开通oss服务