1583. Count Unhappy Friends
Problem Description
You have n
friends (where n
is always even) who need to be paired up. Each friend has a preference list that ranks all other friends from most preferred to least preferred.
Given:
preferences[i]
: A list for friendi
containing all other friends sorted by preference (earlier in the list = more preferred)pairs
: The actual pairings, wherepairs[i] = [xi, yi]
means friendxi
is paired with friendyi
A friend x
becomes unhappy when both of these conditions are true:
- Friend
x
is paired withy
, butx
prefers some other friendu
more thany
- That friend
u
is paired withv
, butu
also prefersx
more than their current partnerv
In other words, x
and u
would both rather be paired with each other than with their current partners.
The task is to count how many friends are unhappy with the given pairing arrangement.
For example, if friend 0 is paired with friend 1, but friend 0 prefers friend 2 more than friend 1, AND friend 2 (who is paired with friend 3) prefers friend 0 more than friend 3, then friend 0 is unhappy. The same logic would also make friend 2 unhappy in this scenario.
The solution uses a dictionary d
to store preference rankings (where d[i][j]
represents the rank/position of friend j
in friend i
's preference list), and another dictionary p
to track who each friend is paired with. It then checks each friend to see if they have a mutual preference with someone other than their current partner.
Intuition
The key insight is that we need to efficiently check if there exists a "mutual preference" situation - where two friends would rather be with each other than their current partners.
To determine if friend x
is unhappy, we need to:
- Find all friends that
x
prefers more than their current partnery
- For each such friend
u
, check ifu
also prefersx
over their current partnerv
The challenge is doing these preference comparisons quickly. If we use the raw preference lists, checking "does x
prefer u
over y
?" would require searching through x
's preference list to find the positions of both u
and y
, then comparing them.
This is where the preprocessing becomes crucial. By converting each preference list into a dictionary that maps friend -> ranking
, we can answer "how much does x
prefer u
?" in O(1) time. Specifically, d[x][u]
gives us the rank of u
in x
's preference list (where smaller rank = more preferred).
With this structure:
- To check if
x
prefersu
overy
: we compared[x][u] < d[x][y]
- To check if
u
prefersx
overv
: we compared[u][x] < d[u][v]
The algorithm then becomes straightforward: for each friend x
paired with y
, we iterate through all friends that x
ranked higher than y
(those with rank less than d[x][y]
). For each such friend u
, we check if the preference is mutual. If we find even one such mutual preference, x
is unhappy and we can stop checking further.
This approach works because we only need to find if at least one "better mutual match" exists for a friend to be unhappy - we don't need to find all such matches.
Solution Approach
The implementation uses two key data structures to efficiently solve the problem:
1. Building the Preference Ranking Dictionary
First, we create a list of dictionaries d
where d[i]
maps each friend to their ranking in friend i
's preference list:
d = [{x: j for j, x in enumerate(p)} for p in preferences]
This transforms the preference lists into a format where we can quickly look up rankings. For example, if preferences[0] = [3, 1, 2]
, then d[0] = {3: 0, 1: 1, 2: 2}
, meaning friend 0 ranks friend 3 at position 0 (most preferred).
2. Creating the Pairing Map
We build a dictionary p
to store who each friend is paired with:
p = {} for x, y in pairs: p[x] = y p[y] = x
This bidirectional mapping allows us to quickly find any friend's partner.
3. Counting Unhappy Friends
For each friend x
from 0 to n-1
:
- Find their current partner:
y = p[x]
- Get the ranking of the current partner:
d[x][y]
- Check all friends that
x
prefers more thany
(those with ranking less thand[x][y]
):for i in range(d[x][y]): u = preferences[x][i] # Get the friend at ranking i
- For each such preferred friend
u
:- Find
u
's current partner:v = p[u]
- Check if
u
also prefersx
over their current partnerv
:if d[u][x] < d[u][v]: ans += 1 break
- If the condition is met,
x
is unhappy. We increment the counter and break (since we only need to find one such mutual preference)
- Find
The time complexity is O(n²) in the worst case, where we might check all pairs of friends. The space complexity is O(n²) for storing the preference rankings dictionary.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with 4 friends (n=4).
Given:
preferences = [[1, 3, 2], [2, 3, 0], [1, 3, 0], [0, 2, 1]]
pairs = [[1, 3], [0, 2]]
This means:
- Friend 0 prefers: 1 > 3 > 2
- Friend 1 prefers: 2 > 3 > 0
- Friend 2 prefers: 1 > 3 > 0
- Friend 3 prefers: 0 > 2 > 1
And the pairings are: (0,2) and (1,3)
Step 1: Build the preference ranking dictionary d
d[0] = {1: 0, 3: 1, 2: 2} # Friend 0 ranks friend 1 at position 0 d[1] = {2: 0, 3: 1, 0: 2} # Friend 1 ranks friend 2 at position 0 d[2] = {1: 0, 3: 1, 0: 2} # Friend 2 ranks friend 1 at position 0 d[3] = {0: 0, 2: 1, 1: 2} # Friend 3 ranks friend 0 at position 0
Step 2: Create the pairing map p
p = {1: 3, 3: 1, 0: 2, 2: 0}
Step 3: Check each friend for unhappiness
Friend 0:
- Current partner: y = p[0] = 2
- Rank of current partner: d[0][2] = 2
- Check friends ranked better than 2 (positions 0 and 1):
- Position 0: u = 1
- u's partner: v = p[1] = 3
- Does friend 1 prefer 0 over 3? d[1][0] = 2, d[1][3] = 1
- Since 2 > 1, friend 1 does NOT prefer 0 over 3
- Position 1: u = 3
- u's partner: v = p[3] = 1
- Does friend 3 prefer 0 over 1? d[3][0] = 0, d[3][1] = 2
- Since 0 < 2, friend 3 DOES prefer 0 over 1
- Friend 0 is unhappy! (break)
- Position 0: u = 1
Friend 1:
- Current partner: y = p[1] = 3
- Rank of current partner: d[1][3] = 1
- Check friends ranked better than 1 (position 0):
- Position 0: u = 2
- u's partner: v = p[2] = 0
- Does friend 2 prefer 1 over 0? d[2][1] = 0, d[2][0] = 2
- Since 0 < 2, friend 2 DOES prefer 1 over 0
- Friend 1 is unhappy! (break)
- Position 0: u = 2
Friend 2:
- Current partner: y = p[2] = 0
- Rank of current partner: d[2][0] = 2
- Check friends ranked better than 2 (positions 0 and 1):
- Position 0: u = 1
- u's partner: v = p[1] = 3
- Does friend 1 prefer 2 over 3? d[1][2] = 0, d[1][3] = 1
- Since 0 < 1, friend 1 DOES prefer 2 over 3
- Friend 2 is unhappy! (break)
- Position 0: u = 1
Friend 3:
- Current partner: y = p[3] = 1
- Rank of current partner: d[3][1] = 2
- Check friends ranked better than 2 (positions 0 and 1):
- Position 0: u = 0
- u's partner: v = p[0] = 2
- Does friend 0 prefer 3 over 2? d[0][3] = 1, d[0][2] = 2
- Since 1 < 2, friend 0 DOES prefer 3 over 2
- Friend 3 is unhappy! (break)
- Position 0: u = 0
Result: All 4 friends are unhappy in this pairing arrangement.
This example illustrates how mutual preferences create unhappiness: Friend 0 and 3 would prefer each other, and friends 1 and 2 would prefer each other, making everyone unhappy with their current pairings.
Solution Implementation
1class Solution:
2 def unhappyFriends(
3 self, n: int, preferences: List[List[int]], pairs: List[List[int]]
4 ) -> int:
5 # Build preference ranking dictionaries for each person
6 # preference_rank[person][friend] = rank (lower rank means higher preference)
7 preference_rank = []
8 for person_preferences in preferences:
9 rank_dict = {}
10 for rank, friend in enumerate(person_preferences):
11 rank_dict[friend] = rank
12 preference_rank.append(rank_dict)
13
14 # Build a dictionary to store each person's paired partner
15 # partner_map[person] = their_partner
16 partner_map = {}
17 for person_x, person_y in pairs:
18 partner_map[person_x] = person_y
19 partner_map[person_y] = person_x
20
21 # Count the number of unhappy people
22 unhappy_count = 0
23
24 # Check each person to see if they are unhappy
25 for person_x in range(n):
26 # Get x's current partner
27 partner_y = partner_map[person_x]
28
29 # Check all people that x prefers over their current partner y
30 rank_of_y = preference_rank[person_x][partner_y]
31 for rank in range(rank_of_y):
32 # Get a person u that x prefers over y
33 preferred_person_u = preferences[person_x][rank]
34
35 # Get u's current partner
36 partner_v = partner_map[preferred_person_u]
37
38 # Check if u also prefers x over their current partner v
39 # If u's rank for x is less than u's rank for v, then u prefers x over v
40 if preference_rank[preferred_person_u][person_x] < preference_rank[preferred_person_u][partner_v]:
41 # Found a case where x and u prefer each other over their current partners
42 # So person x is unhappy
43 unhappy_count += 1
44 break
45
46 return unhappy_count
47
1class Solution {
2 public int unhappyFriends(int n, int[][] preferences, int[][] pairs) {
3 // Build a preference ranking matrix where rankMatrix[i][j] represents
4 // the preference rank of person j in person i's preference list
5 // Lower rank means higher preference (0 = most preferred)
6 int[][] rankMatrix = new int[n][n];
7 for (int person = 0; person < n; ++person) {
8 for (int rank = 0; rank < n - 1; ++rank) {
9 int friend = preferences[person][rank];
10 rankMatrix[person][friend] = rank;
11 }
12 }
13
14 // Store the current partner for each person based on the given pairs
15 int[] currentPartner = new int[n];
16 for (int[] pair : pairs) {
17 int person1 = pair[0];
18 int person2 = pair[1];
19 currentPartner[person1] = person2;
20 currentPartner[person2] = person1;
21 }
22
23 // Count the number of unhappy people
24 int unhappyCount = 0;
25
26 // Check if each person is unhappy
27 for (int personX = 0; personX < n; ++personX) {
28 int personY = currentPartner[personX];
29
30 // Check all people that personX prefers more than their current partner personY
31 // If personX's rank in personY's list is rankMatrix[personX][personY],
32 // we check all people with rank 0 to rankMatrix[personX][personY] - 1
33 for (int rankIndex = 0; rankIndex < rankMatrix[personX][personY]; ++rankIndex) {
34 int personU = preferences[personX][rankIndex];
35 int personV = currentPartner[personU];
36
37 // PersonX is unhappy if:
38 // 1. PersonX prefers personU over personY (already satisfied by the loop condition)
39 // 2. PersonU prefers personX over their current partner personV
40 if (rankMatrix[personU][personX] < rankMatrix[personU][personV]) {
41 ++unhappyCount;
42 break; // Once we find personX is unhappy, no need to check further
43 }
44 }
45 }
46
47 return unhappyCount;
48 }
49}
50
1class Solution {
2public:
3 int unhappyFriends(int n, vector<vector<int>>& preferences, vector<vector<int>>& pairs) {
4 // Build preference rank matrix: preferenceRank[i][j] = rank of person j in person i's preference list
5 // Lower rank means higher preference (0 = most preferred)
6 vector<vector<int>> preferenceRank(n, vector<int>(n));
7 for (int person = 0; person < n; ++person) {
8 for (int rank = 0; rank < n - 1; ++rank) {
9 int friendId = preferences[person][rank];
10 preferenceRank[person][friendId] = rank;
11 }
12 }
13
14 // Store each person's paired partner
15 vector<int> partner(n, 0);
16 for (auto& pair : pairs) {
17 int person1 = pair[0];
18 int person2 = pair[1];
19 partner[person1] = person2;
20 partner[person2] = person1;
21 }
22
23 // Count unhappy friends
24 int unhappyCount = 0;
25 for (int x = 0; x < n; ++x) {
26 int y = partner[x]; // x's current partner
27
28 // Check all people that x prefers more than y
29 for (int rankIndex = 0; rankIndex < preferenceRank[x][y]; ++rankIndex) {
30 int u = preferences[x][rankIndex]; // Person that x prefers more than y
31 int v = partner[u]; // u's current partner
32
33 // Check if u also prefers x more than their current partner v
34 // If true, x is unhappy (x and u would rather be with each other)
35 if (preferenceRank[u][x] < preferenceRank[u][v]) {
36 ++unhappyCount;
37 break; // x is unhappy, no need to check other preferences
38 }
39 }
40 }
41
42 return unhappyCount;
43 }
44};
45
1/**
2 * Counts the number of unhappy friends based on their preferences and current pairings
3 * @param n - Number of friends (even number)
4 * @param preferences - 2D array where preferences[i][j] represents the j-th preferred friend of person i
5 * @param pairs - Array of friend pairs where pairs[i] = [xi, yi] means xi and yi are paired together
6 * @returns Number of unhappy friends
7 */
8function unhappyFriends(n: number, preferences: number[][], pairs: number[][]): number {
9 // Create a preference rank matrix where preferenceRank[i][j] represents
10 // the rank/position of friend j in friend i's preference list
11 // Lower rank means higher preference
12 const preferenceRank: number[][] = Array.from({ length: n }, () => Array(n).fill(0));
13
14 // Build the preference rank matrix
15 for (let person = 0; person < n; ++person) {
16 for (let rank = 0; rank < n - 1; ++rank) {
17 const preferredFriend = preferences[person][rank];
18 preferenceRank[person][preferredFriend] = rank;
19 }
20 }
21
22 // Store current partner for each person
23 const currentPartner: number[] = Array(n).fill(0);
24
25 // Map each person to their current partner
26 for (const [person1, person2] of pairs) {
27 currentPartner[person1] = person2;
28 currentPartner[person2] = person1;
29 }
30
31 let unhappyCount = 0;
32
33 // Check if each person is unhappy
34 for (let x = 0; x < n; ++x) {
35 const y = currentPartner[x];
36 const rankOfCurrentPartner = preferenceRank[x][y];
37
38 // Check all friends that x prefers more than their current partner y
39 for (let rank = 0; rank < rankOfCurrentPartner; ++rank) {
40 const u = preferences[x][rank]; // Friend that x prefers more than y
41 const v = currentPartner[u]; // u's current partner
42
43 // If u also prefers x more than their current partner v,
44 // then x is unhappy
45 if (preferenceRank[u][x] < preferenceRank[u][v]) {
46 ++unhappyCount;
47 break; // x is unhappy, no need to check other friends
48 }
49 }
50 }
51
52 return unhappyCount;
53}
54
Time and Space Complexity
Time Complexity: O(n²)
The algorithm consists of several parts:
- Building the dictionary
d
: This involves iterating throughn
preference lists, each containingn-1
elements. Creating a dictionary for each person takesO(n)
time, so total isO(n²)
. - Building the pairing dictionary
p
: Iterating throughn/2
pairs takesO(n)
time. - Main loop: For each person
x
(n iterations):- In the worst case, we iterate through all friends in their preference list before
y
, which could be up ton-1
friends - For each friend
u
, we perform constant time lookups in dictionariesp
andd
- This gives us
O(n)
per person, resulting inO(n²)
total
- In the worst case, we iterate through all friends in their preference list before
The dominant operation is O(n²)
, giving us an overall time complexity of O(n²)
.
Space Complexity: O(n²)
The space usage breaks down as:
- Dictionary
d
: Storesn
dictionaries, each containingn-1
key-value pairs, usingO(n²)
space total - Dictionary
p
: Storesn
key-value pairs, usingO(n)
space - The input
preferences
array itself usesO(n²)
space (though this is typically not counted as auxiliary space) - Other variables (
ans
, loop variables) useO(1)
space
The dominant space requirement is O(n²)
from dictionary d
, giving us an overall space complexity of O(n²)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Break Statement Placement
A critical pitfall is forgetting or misplacing the break
statement after finding that a person is unhappy. Once we determine that person x
is unhappy (by finding even one person u
with whom there's a mutual preference), we must immediately break out of the inner loop.
Incorrect Implementation:
for rank in range(rank_of_y):
preferred_person_u = preferences[person_x][rank]
partner_v = partner_map[preferred_person_u]
if preference_rank[preferred_person_u][person_x] < preference_rank[preferred_person_u][partner_v]:
unhappy_count += 1
# Missing break here - would count x multiple times!
Why it's wrong: Without the break, if person x
has mutual preferences with multiple other people, they would be counted as unhappy multiple times. The problem asks for the number of unhappy friends, not the number of unhappy relationships.
Correct Implementation:
for rank in range(rank_of_y):
preferred_person_u = preferences[person_x][rank]
partner_v = partner_map[preferred_person_u]
if preference_rank[preferred_person_u][person_x] < preference_rank[preferred_person_u][partner_v]:
unhappy_count += 1
break # Critical: stop checking once we know x is unhappy
2. Incorrectly Building the Preference Ranking Dictionary
Another common mistake is confusing the index with the value when building the ranking dictionary.
Incorrect Implementation:
# Wrong: Maps rank to friend instead of friend to rank
preference_rank = []
for person_preferences in preferences:
rank_dict = {}
for rank, friend in enumerate(person_preferences):
rank_dict[rank] = friend # Wrong mapping direction!
preference_rank.append(rank_dict)
Why it's wrong: This creates a dictionary that tells us "who is at rank i" rather than "what rank does friend j have". We need to look up rankings by friend ID, not the other way around.
Correct Implementation:
# Correct: Maps friend to their rank
preference_rank = []
for person_preferences in preferences:
rank_dict = {}
for rank, friend in enumerate(person_preferences):
rank_dict[friend] = rank # Friend -> Rank mapping
preference_rank.append(rank_dict)
3. Off-by-One Error in Preference Comparison
Be careful with the inequality comparisons when checking preferences.
Incorrect Implementation:
# Wrong: Using <= instead of < if preference_rank[preferred_person_u][person_x] <= preference_rank[preferred_person_u][partner_v]: unhappy_count += 1 break
Why it's wrong: Using <=
would incorrectly count cases where u
is indifferent between x
and their current partner v
(same ranking). The problem requires that both people strictly prefer each other over their current partners.
Correct Implementation:
# Correct: Strict inequality ensures u truly prefers x over v if preference_rank[preferred_person_u][person_x] < preference_rank[preferred_person_u][partner_v]: unhappy_count += 1 break
Which technique can we use to find the middle of a linked list?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!