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

 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;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值