java---树与图的DFS深度优先遍历_树的重心(每日一道算法2022.8.23)

注意事项:
代码涉及到手写单链表,可以看我之前的文章:java单链表_数组模拟

题目:
给定一颗树,树中包含 n 个结点(编号 1∼n)和 n−1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

输入格式
第一行包含整数 n,表示树的结点数。
接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。

输出格式
输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。

数据范围
1≤n≤1e5

输入:
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出:
4
public class 树与图的DFS深度优先遍历_树的重心 {
    //初始化, h数组存储所有的链表head,e存储链表元素,ne存储链表的下一位指向,st判断当前节点有没有被走过
    //e和ne都是开N的二倍,ans存储的是每一个连通块最大值中的最小值
    public static int N = 1000010, M = N*2, index = 0, ans = N, n;
    public static int[] h = new int[N], e = new int[M], ne = new int[N];
    public static boolean[] st = new boolean[N];

    public static void main(String[] args) throws IOException {
        //初始化,h数组也就是所有的head设置为-1
        Arrays.fill(h, -1);
        Scanner in = new Scanner(System.in);
        n = in.nextInt();

        //因为是双向图,所以建立连接的时候,需要两个点互相指向
        for (int i = 0; i<n-1; i++){
            int x = in.nextInt(), y = in.nextInt();
            insert_to_head(x, y);
            insert_to_head(y, x);
        }

        dfs(1);

        System.out.println(ans);
    }

    //dfs返回以x为根节点的子树中点的数量
    public static int dfs(int x) {
        //首先st将当前点标记为走过了,sum存储的是当前点的子节点数量包括自己
        //res存储删除当前点后的每一个连通块的最大值
        st[x] = true;
        int sum = 1, res = 0;
        for (int i = h[x]; i!=-1; i = ne[i]) {  //遍历当前节点所连接的所有节点
            int j = e[i];
            if (!st[j]) {                   //判断这个点有没有被走过
                int s = dfs(j);
                res = Math.max(res, s);
                sum += s;
            }
        }
        //res更新为res和之前节点数量中较大的
        //ans更新为当前res和之前ans中较小的
        res = Math.max(res, n - sum);
        ans = Math.min(res, ans);
        return sum;
    }
    
    //在头节点处插入元素,是单链表模拟的知识
    public static void insert_to_head(int x, int y) {
        e[index] = y;
        ne[index] = h[x];
        h[x] = index++;
    }
}

思路:
就拿输入为例子,图画出来是这样的:
请添加图片描述
当我们将1这个点去掉的话,剩下3个连通块的节点数量分别为1,4,3,最大的是4,
然后对每一个点都去找到将它去掉后的剩余连通块中节点数量的最大值,再从这些最大值中找到最小值,就是答案

同时还需要注意一点,由于我们只进行一次dfs,那么会产生一个问题:
比如1为根节点,那么(7)(4639)(285)这三个区间是会被计算的,
进入(4639)这个区间,4会被视为根节点,向下还剩两个区间(6)(39),

大家可能已经发现问题了,如果按照这样计算,去除4这个根节点后本该有三个区间(71285)(6)(39),但我们这里向下dfs只能算到下面的两个区间(6)(39),但想得到之前的那个区间的节点数也很简单,
就是用 总数n 减去 从4为根节点的区间的节点个数sum 即可,
然后再在这几个值之间取max就可以啦。

声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值