2092. 找出知晓秘密的所有专家 力扣 || DFS || 并查集

这篇博客介绍了如何通过深度优先搜索(DFS)和并查集这两种算法来解决一个有趣的数学问题:在一系列会议中,如果某人知道了秘密,那么与他会面的所有人都会得知这一秘密。文章详细解析了两种解题思路,包括建图DFS的方法和并查集的实现,并提供了相应的Java代码实现。这两种方法都能够有效地找出在所有会议结束后,知晓秘密的所有专家人数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 2092 找出知晓秘密的所有专家

 

题目意思,刚开始0,firstPeople,知晓秘密。meetings[i] = [xi,yi,timei]表示xi,yi在timei时刻开会,会议结束时间可以理解为同时结束,一个人可以同时参加多个会议,当一个人在某时刻知道了秘密,那么他会立马将秘密告知与他一同开会的人,求会议结束后有多少人知道了秘密。

第一种解法:DFS建图

思路,先将会议按照时间排序,(1)最容易想到的情况,1-2,2-3,3-4,4-5(如果1知道了秘密,那么这些人也都知道了秘密)(2)可能存在这种情况,1-2,2-3,3-4,4-5(同一时间点的会议,5知道了秘密,那么前面这些人也都知道了秘密;(3)也可能存在这种情况,1-2,2-3,3-4,2-6,(其中4知道秘密)

只要考虑了这三种情况,基本就能把题目做出来。

建图和并查集的解法原理差不多。建图,每个时间点的会议建一个双向图,当图建完,从知晓秘密(本个时间点的会议,如果算这个时间点及之前的,会超时,而且没有必要,仔细想想为啥?)的人出发,能达到的另外一个人也将知道秘密。

第一种解法,建图DFS

class Solution {
    public static int []pre;
    public ArrayList<Integer> ans = null;

    public List<Integer> findAllPeople(int n, int[][] meetings, int firstPerson) {
        pre = new int[1000000 + 5];
        pre[0] = 1;
        pre[firstPerson] = 1;

        ans = new ArrayList<>();
        ans.add(0);
        ans.add(firstPerson);

        // 0 将数据传给firstPerson
        // 然后开始按照时间开始排序
        Arrays.sort(meetings, (ints, t1) -> {
            return ints[2] - t1[2];
        });

        HashMap<Integer, ArrayList<Integer>> hashMap = new HashMap<>();
        ArrayList<Integer> list = null;
        HashSet<Integer> prophet = new HashSet<Integer>();
        int time = -1;
        for (int i = 0; i < meetings.length; i++) {
            // 当不在一个时间点上的时候,开始计算
            if (time != meetings[i][2]) {
                for (Integer integer : prophet) {
                    dfs(integer, hashMap);
                }
                time = meetings[i][2];
                hashMap.clear();
                prophet.clear();
            }

            // 这里需要考虑到点与点之前联通的情况
            list = hashMap.getOrDefault(meetings[i][0], new ArrayList<Integer>());
            list.add(meetings[i][1]);
            hashMap.put(meetings[i][0], list);

            list = hashMap.getOrDefault(meetings[i][1], new ArrayList<Integer>());
            list.add(meetings[i][0]);
            hashMap.put(meetings[i][1], list);

            // 记录知道秘密的人
            if (pre[meetings[i][0]] == 1) prophet.add(meetings[i][0]);
            if (pre[meetings[i][1]] == 1) prophet.add(meetings[i][1]);
        }

        // 这个操作并不会造成多余,还要计算最后一个时间段
        for (Integer integer : prophet) {
            dfs(integer, hashMap);
        }
        return ans;
    }

    public void dfs(int now, HashMap<Integer, ArrayList<Integer>> mp) {
        ArrayList<Integer> list = mp.get(now);
        if (list == null || pre[now] != 1) return;

        for (int i = 0; i < list.size(); i++) {
            if (pre[list.get(i)] != 1) {
                pre[list.get(i)] = 1;
                ans.add(list.get(i));
                dfs(list.get(i), mp);
            }
        }
    }
}

第二种解法,并查集

思路:首先判断一个时间点,在这个时间点,xi,yi,timei,让xi,yi合并在一个集合,当这个时间点的所有xi,yi都合并在了一块儿,开始进行判断,如果xi或者yi的根与0的根不一样,那么表示这个时间点其不知道秘密,然后将其还原(知道秘密的不需要还原)。

    public static int []pre;
    
    public List<Integer> findAllPeople(int n, int[][] meetings, int firstPerson) {
        pre = new int[n + 5];
        // 这里将每个人指向自己
        for (int i = 0; i < n; i++) {
            pre[i] = i;
        }
        // 用来记录个时间点参与会议的人
        Set<Integer> set = new HashSet<>();
        // 下面将0 firstPerson加入set

        union(0, firstPerson);

        Arrays.sort(meetings, (ints, t1) -> { return ints[2] - t1[2]; });

        int time = -1;
        for (int i = 0; i < meetings.length; i++) {
            if (time != meetings[i][2]) {
                for (Integer integer : set) {
                    // 记得还原回去
                    if (find(integer) != find(0)) pre[integer] = integer;
                }
                time = meetings[i][2];
                set.clear();
            }
            // 合并
            union(meetings[i][0], meetings[i][1]);
            set.add(meetings[i][0]);
            set.add(meetings[i][1]);
        }
        // 最后一次会议可以不用还原,直接计算就可以了,指向pre[0]就表示知晓秘密
        for (Integer integer : set) {
            if (find(integer) != find(0)) pre[integer] = integer;
        }

        set.clear();
        set.add(0);
        for (int i = 1; i < n; i++) {
            if (find(i) == find(0)) set.add(i);
        }
        return new ArrayList<>(set);
    }

    // 查找根节点 + 路径压缩
    public int find(int x) {
        int a = x;
        // 返回根节点
        while (pre[x] != x) x = pre[x];
        // 路径压缩
        while (a != pre[a]) {
            int z = a;
            a = pre[a];
            pre[z] = x;
        }
        return x;
    }
    // 合并
    public void union(int a, int b) {
        int fa = find(a);
        int fb = find(b);
        if (fa != fb) pre[fa] = fb;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值