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] = [x_i, y_i]
means that person x_i
and person y_i
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] = [u_j, v_j]
is a friend request between person u_j
and person v_j
.
A friend request is successful if u_j
and v_j
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, u_j
and v_j
become direct friends for all future friend requests.
Return a boolean array result
, where each result[j]
is true
if the j^th
friend request is successful or false
if it is not.
Note: If u_j
and v_j
are already direct friends, the request is still successful.
For example:
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).
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).
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).
Can you implement a function that takes n
, restrictions
, and requests
as input and returns the boolean array result
as described above?
The brute force approach would be to simulate each friend request and, after each request, check if any of the restrictions are violated. This involves creating a graph representing the friendships and, for each restriction, performing a graph traversal (e.g., DFS or BFS) to see if a path exists between the restricted individuals.
result
of the same length as requests
to store the success status of each friend request.[u, v]
in requests
:
result[i] = true
).[x, y]
in restrictions
:
x
and y
in the temporary graph using DFS or BFS.result[i] = false
and break the inner loop (checking restrictions).result[i]
is still true, add the friendship [u, v]
to the graph.def solve():
def check_path(graph, start, end, visited):
if start == end:
return True
visited[start] = True
for neighbor in graph[start]:
if not visited[neighbor]:
if check_path(graph, neighbor, end, visited):
return True
return False
def friend_requests(n, restrictions, requests):
result = [False] * len(requests)
friends = {i: {i} for i in range(n)}
for i, (u, v) in enumerate(requests):
successful = True
# Simulate adding the edge and check restrictions
temp_friends = {node: set(neighbors) for node, neighbors in friends.items()}
temp_friends[u] = set(friends[u])
temp_friends[v] = set(friends[v])
# Union
u_group = temp_friends[u]
v_group = temp_friends[v]
group = u_group.union(v_group)
for person in group:
temp_friends[person] = group
for x, y in restrictions:
visited = [False] * n
if x in group and y in group:
successful = False
break
if successful:
friends[u] = set(friends[u])
friends[v] = set(friends[v])
# Union
u_group = friends[u]
v_group = friends[v]
group = u_group.union(v_group)
for person in group:
friends[person] = group
result[i] = True
return result
n = 3
restrictions = [[0, 1]]
requests = [[0, 2], [2, 1]]
print(friend_requests(n, restrictions, requests))
n = 3
restrictions = [[0, 1]]
requests = [[1, 2], [0, 2]]
print(friend_requests(n, restrictions, requests))
n = 5
restrictions = [[0, 1], [1, 2], [2, 3]]
requests = [[0, 4], [1, 2], [3, 1], [3, 4]]
print(friend_requests(n, restrictions, requests))
solve()
m
is the number of requests, r
is the number of restrictions, and n
is the number of people. The n^2
comes from checking for a path using DFS/BFS in the worst case.A more efficient approach is to use the Disjoint Set Union (DSU) data structure (also known as Union-Find). DSU allows us to keep track of connected components (friend groups) and efficiently determine if two people belong to the same group. We can use path compression and union by rank for further optimization.
parent
array where parent[i] = i
for all i
from 0 to n-1
. This means initially each person is in their own group.find(x)
function to find the representative (root) of the group that x
belongs to, with path compression.union(x, y)
function to merge the groups of x
and y
.[u, v]
in requests
:
u
and v
using the find
function: rootU = find(u)
and rootV = find(v)
.u
and v
friends would violate any restrictions. For each restriction [x, y]
, find the representatives of x
and y
. If (find(x) == rootU and find(y) == rootV) or (find(x) == rootV and find(y) == rootU)
, then making u
and v
friends would indirectly connect x
and y
, violating the restriction. If any restriction is violated, the request fails.union(u, v)
to merge the groups of u
and v
and set result[i] = true
. Otherwise, set result[i] = false
.def solve():
def find(parent, i):
if parent[i] == i:
return i
parent[i] = find(parent, parent[i])
return parent[i]
def union(parent, x, y):
root_x = find(parent, x)
root_y = find(parent, y)
parent[root_x] = root_y
def friend_requests(n, restrictions, requests):
parent = list(range(n))
result = [False] * len(requests)
for i, (u, v) in enumerate(requests):
root_u = find(parent, u)
root_v = find(parent, v)
valid = True
for x, y in restrictions:
root_x = find(parent, x)
root_y = find(parent, y)
if (root_x == root_u and root_y == root_v) or (root_x == root_v and root_y == root_u):
valid = False
break
if valid:
union(parent, u, v)
result[i] = True
return result
n = 3
restrictions = [[0, 1]]
requests = [[0, 2], [2, 1]]
print(friend_requests(n, restrictions, requests))
n = 3
restrictions = [[0, 1]]
requests = [[1, 2], [0, 2]]
print(friend_requests(n, restrictions, requests))
n = 5
restrictions = [[0, 1], [1, 2], [2, 3]]
requests = [[0, 4], [1, 2], [3, 1], [3, 4]]
print(friend_requests(n, restrictions, requests))
solve()
m
is the number of requests, r
is the number of restrictions, and α(n) is the inverse Ackermann function, which grows very slowly and can be considered almost constant for practical input sizes. The m
comes from iterating through the requests. For each request, we iterate through the restrictions (r) and perform find operations (α(n)).n
is the number of people, to store the parent
array for the DSU data structure.u
and v
are already friends (belong to the same connected component), the request is still considered successful.u
or v
are out of range [0, n-1]. The code assumes the input is valid as per the constraints.