算法小练习之处理含限制条件的好友请求

201 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

1、前言

每天一个算法小练习,本篇使用Java实现。

2、题目描述

  给你一个整数 n,表示网络上的用户数目。每个用户按从 0 到 n - 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。

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

2.1、示例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.2、示例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、解题思路

使用并查集维护朋友关系,每个集合内都是直接朋友或者间接朋友。对于每一条好友请求, 先假设两者可以做朋友,并连接。然后再遍历所有限制条件,如果违反就添加失败,并把并查集还原回去,否则添加好友成功;

3.1.1、实现代码

public boolean[] friendRequests(int n, int[][] restrictions, int[][] requests) {
    boolean[] res = new boolean[requests.length];
    // 并查集 
    UnionFind uf = new UnionFind(n);
    for(int j = 0; j < requests.length; j++){
        int u = requests[j][0], v = requests[j][1];
        UnionFind cacheUF = new UnionFind(n);
        cacheUF.parent = Arrays.copyOfRange(uf.parent, 0, n);
        cacheUF.rank = Arrays.copyOfRange(uf.rank, 0, n);
        cacheUF.size = uf.size;
        // 先假设两人可以做朋友
        uf.union(u, v);
        boolean canSuccess = true;
        for(int[] restriction: restrictions){
            // 遍历全部限制来判断是否有矛盾
            if(uf.isConnected(restriction[0], restriction[1])){
                // 如果有矛盾则说明该请求不能实现,还原操作
                uf = cacheUF;           
                canSuccess = false;
                break;
            }
        }
        if(canSuccess){
            res[j] = true;
        }
    }
    return res;
}
}

class UnionFind {
public int[] parent;
//每个连通区域的大小
public int[] rank;
public int size;

public UnionFind(int n) {
    parent = new int[n];
    rank = new int[n];
    for(int i = 0; i < n; i++){
        parent[i] = i;
        rank[i] = 1;
    }
    size = n;
}

public int findP(int x){
    // 找到 x 的父亲节点
    while(x != parent[x]){
        parent[x] = parent[parent[x]];
        x = parent[x];
    }
    return x;
}

public void union(int x, int y) {
    int rootX = findP(x);
    int rootY = findP(y);
    if(rootX != rootY){
        // 连接
        if(rank[rootX] < rank[rootY]){
            parent[rootX] = rootY;
        }else if(rank[rootX] > rank[rootY]){
            parent[rootY] = rootX;
        }else{
            parent[rootX] = rootY;
            rank[rootY]++;
        }
        size--;
    }
}

public boolean isConnected(int x, int y) {
    // 判断 x 和 y 是否连通
    return findP(x) == findP(y);
}

public int getCount() {
    return size;
}

3.1.2、执行结果

image.png

好了、本期就先介绍到这里,有什么需要交流的,大家可以随时私信我。😊