X

LEETCODE 2076. Process Restricted Friend Requests 解题思路分析

题目大意:

处理含限制条件的好友请求

给你一个整数 n ,表示网络上的用户数目。每个用户按从 0n - 1 进行编号。

给你一个下标从 0 开始的二维整数数组 restrictions ,其中 restrictions[i] = [xi, yi] 意味着用户 xi 和用户 yi 不能 成为 朋友 ,不管是 直接 还是通过其他用户 间接 。

最初,用户里没有人是其他用户的朋友。给你一个下标从 0 开始的二维整数数组 requests 表示好友请求的列表,其中 requests[j] = [uj, vj] 是用户 uj 和用户 vj 之间的一条好友请求。

如果 uj 和 vj 可以成为 朋友 ,那么好友请求将会 成功 。每个好友请求都会按列表中给出的顺序进行处理(即,requests[j] 会在 requests[j + 1] 前)。一旦请求成功,那么对所有未来的好友请求而言, uj 和 vj 将会 成为直接朋友 。

返回一个 布尔数组 result ,其中元素遵循此规则:如果第 j 个好友请求 成功,那么 result[j]就是true;否则,为false

注意:如果 ujvj 已经是直接朋友,那么他们之间的请求将仍然 成功

示例 1:

输入:n = 3, restrictions = [[0,1]], requests = [[0,2],[2,1]]
输出:[true,false]
解释:
请求 0 :用户 0 和 用户 2 可以成为朋友,所以他们成为直接朋友。 
请求 1 :用户 2 和 用户 1 不能成为朋友,因为这会使 用户 0 和 用户 1 成为间接朋友 (1--2--0) 。

示例 2:

输入:n = 3, restrictions = [[0,1]], requests = [[1,2],[0,2]]
输出:[true,false]
解释:
请求 0 :用户 1 和 用户 2 可以成为朋友,所以他们成为直接朋友。 
请求 1 :用户 0 和 用户 2 不能成为朋友,因为这会使 用户 0 和 用户 1 成为间接朋友 (0--2--1) 。

示例 3:

输入:n = 5, restrictions = [[0,1],[1,2],[2,3]], requests = [[0,4],[1,2],[3,1],[3,4]]
输出:[true,false,true,false]
解释:
请求 0 :用户 0 和 用户 4 可以成为朋友,所以他们成为直接朋友。 
请求 1 :用户 1 和 用户 2 不能成为朋友,因为他们之间存在限制。
请求 2 :用户 3 和 用户 1 可以成为朋友,所以他们成为直接朋友。 
请求 3 :用户 3 和 用户 4 不能成为朋友,因为这会使 用户 0 和 用户 1 成为间接朋友 (0--4--3--1) 。

提示:

2 <= n <= 1000
0 <= restrictions.length <= 1000
restrictions[i].length == 2
0 <= xi, yi <= n – 1
xi != yi
1 <= requests.length <= 1000
requests[j].length == 2
0 <= uj, vj <= n – 1
uj != vj


解题思路分析:

这是一道描述朋友圈的题目。在这个圈子中,朋友的敌人就是敌人,朋友的朋友还是朋友,每个圈子一定要做到团结一致,同仇敌忾!

对于这种类似划分集合类型的题目,我第一反应就是使用并查集Union-Find来解题。我们可以试想最终会划分为N个圈子,而每个圈子中都有一位大哥和一众小弟,并且大家都有相同的敌人和朋友。解题时,我们定义一个数组root[],root[i]代表i的大哥是谁。起初在没有任何人结盟的时候,每个人就是一个圈子,自己也是圈子的大哥。另外我们还需要定义两个集合,用于存储每个圈子的朋友列表和敌人列表。

我们首先开始初始化每个圈子的敌人列表,题目给出一个restrictions的二维数组。我们循环该数组,比如[1, 2]是敌对关系,那么,我们将1的敌人列表中存入2,同时将2的敌人列表中存入1。以此类推。

其次我们还需要初始化一下朋友列表,起初,每个人(圈子)的朋友只有自己。

接下来开始正式处理requests数组,即好友请求列表。首先求出双方圈子的大哥(起初大哥就是自己),如果大哥一致的话,说明大家本是一个圈子的自己人,该请求成功。如果大哥不一致时我们就要做背景调查了,我们可以使用任意一方的朋友列表去比对另外一方的敌人列表,如果一方的朋友出现在另一方的敌人列表中的话,那么对不起我们两方水火不相容,请求失败。反之如果双方没有什么恩怨,请求成功,同时,两个人成为好友实际上代表两个圈子结成一个更大的联盟。此时需要将各自的好友列表和敌人列表加入到对方的列表之中。

最后看下实现代码:

class Solution {
    int[] root; // 记录每个人的大哥
    Map<Integer, Set<Integer>> friendMap = new HashMap<>(); // 好友列表
    Map<Integer, Set<Integer>> enemyMap = new HashMap<>(); // 敌人列表
    public boolean[] friendRequests(int n, int[][] restrictions, int[][] requests) {
        boolean[] res = new boolean[requests.length]; // 返回结果
        root = new int[n]; // 初始化root数组
        for(int i=0;i<n;i++) root[i] = i; // 起初没有结盟的时候,各自为一组,圈子大哥也是自己
        for(int[] restriction : restrictions){ // 循环敌对关系,记录每个人的敌对关系
            Set enemySet1 = enemyMap.getOrDefault(restriction[0], new HashSet<>());
            enemySet1.add(restriction[1]);
            enemyMap.put(restriction[0], enemySet1);
            
            Set enemySet2 = enemyMap.getOrDefault(restriction[1], new HashSet<>());
            enemySet2.add(restriction[0]);
            enemyMap.put(restriction[1], enemySet2);         
        }
        for(int i=0;i<n;i++){ // 初始化好友列表,起初每个人的好友只有自己
            Set<Integer> set = new HashSet<>();
            set.add(i);
            friendMap.put(i, set);
        }
        
        for(int i = 0; i< requests.length; i++){ // 循环好友请求列表
            int root1 = root(requests[i][0]); // 请求方的圈子大哥
            int root2 = root(requests[i][1]); // 被请求方的圈子大哥
            if(root1 == root2) { // 如果大哥相同,表示是自己人
                res[i] = true; // 请求成功
                continue;
            }
            // 双方的好友列表
            Set<Integer> p1Friends = friendMap.getOrDefault(root1, new HashSet<>());
            Set<Integer> p2Friends = friendMap.getOrDefault(root2, new HashSet<>());
            // 双方的敌人列表
            Set<Integer> p1Enemies = enemyMap.getOrDefault(root1, new HashSet<>());
            Set<Integer> p2Enemies = enemyMap.getOrDefault(root2, new HashSet<>());
            // 中间变量,双方是否能成为朋友
            boolean canBeFriends = true;
            for(int p1Friend : p1Friends){ // 循环其中一方的朋友列表
                if(p2Enemies.contains(p1Friend)){ // 如果另外一方的敌人列表中包含当前朋友
                    canBeFriends = false; // 请求失败
                    break;
                }
            }
            if(!canBeFriends){ // 如果请求失败,继续处理下一个请求
                res[i] = false;
                continue;
            }
            // 接下来代码可以请求成功
            // 合并双方的好友列表
            p1Friends.addAll(p2Friends);
            friendMap.put(root1, p1Friends);
            friendMap.put(root2, p1Friends);
            // 合并双方的敌人列表
            p1Enemies.addAll(p2Enemies);
            enemyMap.put(root1, p1Enemies);
            enemyMap.put(root2, p1Enemies);
            root[root1] = root2;
            
            res[i] = true;
        }
        return res;
    }
    int root(int index){
        if(root[index] == index) return index;
        return root(root[index]);
    }
}

本题解法执行时间为65ms。

Runtime: 65 ms, faster than 87.57% of Java online submissions for Process Restricted Friend Requests.Memory Usage: 62.2 MB, less than 10.43% of Java online submissions for Process Restricted Friend Requests.

本网站文章均为原创内容,并可随意转载,但请标明本文链接
如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: https://leetcode.jp/leetcode-2076-process-restricted-friend-requests-解题思路分析/
Categories: leetcode
kwantong: