Process Restricted Friend Requests
Problem
You are given an integer n indicating the number of people in a network.
Each person is labeled from 0 to n - 1.
You are also given a 0-indexed 2D integer array restrictions, where
restrictions[i] = [xi, yi] means that person xi and person yi cannot become friends ,**** either directly or indirectly through other people.
Initially, no one is friends with each other. You are given a list of friend requests as a 0-indexed 2D integer array requests, where requests[j] = [uj, vj] is a friend request between person uj and person vj.
A friend request is successful if uj and vj can be friends. Each friend request is processed in the given order (i.e., requests[j] occurs before requests[j + 1]), and upon a successful request, uj and vj
become direct friends for all future friend requests.
Return _aboolean array _result,where eachresult[j]istrue if thejth _friend request issuccessful or _false if it is not.
Note: If uj and vj are already direct friends, the request is still
successful.
Examples
Example 1
Input: n = 3, restrictions = [[0,1]], requests = [[0,2],[2,1]]
Output: [true,false]
Explanation: Request 0: Person 0 and person 2 can be friends, so they become direct friends.
Request 1: Person 2 and person 1 cannot be friends since person 0 and person 1 would be indirect friends (1--2--0).
Example 2
Input: n = 3, restrictions = [[0,1]], requests = [[1,2],[0,2]]
Output: [true,false]
Explanation: Request 0: Person 1 and person 2 can be friends, so they become direct friends.
Request 1: Person 0 and person 2 cannot be friends since person 0 and person 1 would be indirect friends (0--2--1).
Example 3
Input: n = 5, restrictions = [[0,1],[1,2],[2,3]], requests = [[0,4],[1,2],[3,1],[3,4]]
Output: [true,false,true,false]
Explanation: Request 0: Person 0 and person 4 can be friends, so they become direct friends.
Request 1: Person 1 and person 2 cannot be friends since they are directly restricted.
Request 2: Person 3 and person 1 can be friends, so they become direct friends.
Request 3: Person 3 and person 4 cannot be friends since person 0 and person 1 would be indirect friends (0--4--3--1).
Constraints
2 <= n <= 10000 <= restrictions.length <= 1000restrictions[i].length == 20 <= xi, yi <= n - 1xi != yi1 <= requests.length <= 1000requests[j].length == 20 <= uj, vj <= n - 1uj != vj
Solution
Method 1 – Union-Find with Rollback
Intuition
We use Union-Find (Disjoint Set Union) to track friend groups. For each request, we check if merging the groups would violate any restriction. If not, we merge; otherwise, we skip. To avoid affecting future requests, we can simulate the merge and roll back if needed.
Approach
- For each request, find the roots of both people.
- If already in the same group, accept.
- Otherwise, check if merging would violate any restriction (i.e., if any restricted pair would end up in the same group).
- If not, merge and accept; else, reject.
Code
C++
class Solution {
public:
vector<bool> friendRequests(int n, vector<vector<int>>& restrictions, vector<vector<int>>& requests) {
vector<int> parent(n);
iota(parent.begin(), parent.end(), 0);
function<int(int)> find = [&](int x) { return parent[x] == x ? x : parent[x] = find(parent[x]); };
vector<bool> res;
for (auto& req : requests) {
int u = find(req[0]), v = find(req[1]);
bool ok = true;
if (u != v) {
for (auto& r : restrictions) {
int x = find(r[0]), y = find(r[1]);
if ((u == x && v == y) || (u == y && v == x)) {
ok = false; break;
}
}
}
if (ok) {
res.push_back(true);
if (u != v) parent[u] = v;
} else {
res.push_back(false);
}
}
return res;
}
};
Go
func friendRequests(n int, restrictions [][]int, requests [][]int) []bool {
parent := make([]int, n)
for i := range parent { parent[i] = i }
var find func(int) int
find = func(x int) int { if parent[x] != x { parent[x] = find(parent[x]) }; return parent[x] }
res := make([]bool, 0, len(requests))
for _, req := range requests {
u, v := find(req[0]), find(req[1])
ok := true
if u != v {
for _, r := range restrictions {
x, y := find(r[0]), find(r[1])
if (u == x && v == y) || (u == y && v == x) { ok = false; break }
}
}
if ok {
res = append(res, true)
if u != v { parent[u] = v }
} else {
res = append(res, false)
}
}
return res
}
Java
import java.util.*;
class Solution {
public boolean[] friendRequests(int n, int[][] restrictions, int[][] requests) {
int[] parent = new int[n];
for (int i = 0; i < n; ++i) parent[i] = i;
boolean[] res = new boolean[requests.length];
for (int k = 0; k < requests.length; ++k) {
int u = find(parent, requests[k][0]), v = find(parent, requests[k][1]);
boolean ok = true;
if (u != v) {
for (int[] r : restrictions) {
int x = find(parent, r[0]), y = find(parent, r[1]);
if ((u == x && v == y) || (u == y && v == x)) { ok = false; break; }
}
}
if (ok) {
res[k] = true;
if (u != v) parent[u] = v;
} else {
res[k] = false;
}
}
return res;
}
private int find(int[] parent, int x) {
if (parent[x] != x) parent[x] = find(parent, parent[x]);
return parent[x];
}
}
Kotlin
class Solution {
fun friendRequests(n: Int, restrictions: Array<IntArray>, requests: Array<IntArray>): BooleanArray {
val parent = IntArray(n) { it }
val res = BooleanArray(requests.size)
fun find(x: Int): Int {
if (parent[x] != x) parent[x] = find(parent[x])
return parent[x]
}
for ((k, req) in requests.withIndex()) {
val u = find(req[0]); val v = find(req[1])
var ok = true
if (u != v) {
for (r in restrictions) {
val x = find(r[0]); val y = find(r[1])
if ((u == x && v == y) || (u == y && v == x)) { ok = false; break }
}
}
if (ok) {
res[k] = true
if (u != v) parent[u] = v
} else {
res[k] = false
}
}
return res
}
}
Python
from typing import List
class Solution:
def friendRequests(self, n: int, restrictions: List[List[int]], requests: List[List[int]]) -> List[bool]:
parent = list(range(n))
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
res = []
for u, v in requests:
ru, rv = find(u), find(v)
ok = True
if ru != rv:
for x, y in restrictions:
rx, ry = find(x), find(y)
if (ru == rx and rv == ry) or (ru == ry and rv == rx):
ok = False
break
if ok:
res.append(True)
if ru != rv:
parent[ru] = rv
else:
res.append(False)
return res
Rust
impl Solution {
pub fn friend_requests(n: i32, restrictions: Vec<Vec<i32>>, requests: Vec<Vec<i32>>) -> Vec<bool> {
let n = n as usize;
let mut parent: Vec<usize> = (0..n).collect();
fn find(parent: &mut Vec<usize>, x: usize) -> usize {
if parent[x] != x { parent[x] = find(parent, parent[x]); }
parent[x]
}
let mut res = Vec::new();
for req in requests.iter() {
let u = find(&mut parent, req[0] as usize);
let v = find(&mut parent, req[1] as usize);
let mut ok = true;
if u != v {
for r in restrictions.iter() {
let x = find(&mut parent, r[0] as usize);
let y = find(&mut parent, r[1] as usize);
if (u == x && v == y) || (u == y && v == x) { ok = false; break; }
}
}
if ok {
res.push(true);
if u != v { parent[u] = v; }
} else {
res.push(false);
}
}
res
}
}
TypeScript
class Solution {
friendRequests(n: number, restrictions: number[][], requests: number[][]): boolean[] {
const parent = Array.from({length: n}, (_, i) => i);
function find(x: number): number {
if (parent[x] !== x) parent[x] = find(parent[x]);
return parent[x];
}
const res: boolean[] = [];
for (const [u, v] of requests) {
const ru = find(u), rv = find(v);
let ok = true;
if (ru !== rv) {
for (const [x, y] of restrictions) {
const rx = find(x), ry = find(y);
if ((ru === rx && rv === ry) || (ru === ry && rv === rx)) { ok = false; break; }
}
}
if (ok) {
res.push(true);
if (ru !== rv) parent[ru] = rv;
} else {
res.push(false);
}
}
return res;
}
}
Complexity
- ⏰ Time complexity:
O(R * K)where R is the number of requests and K is the number of restrictions (since for each request we may check all restrictions). - 🧺 Space complexity:
O(N)for the union-find structure.