277. Find the Celebrity π
Problem Description
You're at a party with n
people labeled from 0
to n - 1
. Among these people, there might be exactly one celebrity. A celebrity is defined as someone who:
- Is known by all other
n - 1
people at the party - Does not know any of the other people
Your task is to identify who the celebrity is, or determine that no celebrity exists at the party.
You can only gather information by asking questions in the form: "Does person A know person B?" This is done through a helper function knows(a, b)
which returns true
if person a
knows person b
, and false
otherwise.
The goal is to minimize the number of questions asked (in terms of asymptotic complexity) while finding the celebrity.
The function should return:
- The celebrity's label (a number from
0
ton - 1
) if a celebrity exists -1
if there is no celebrity at the party
Note that you don't have direct access to the underlying relationship graph. You can only use the knows(a, b)
function to query relationships between people.
Intuition
The key insight is that when we ask if person a
knows person b
, we can eliminate one candidate from being the celebrity:
- If
a
knowsb
, thena
cannot be the celebrity (celebrities don't know anyone) - If
a
doesn't knowb
, thenb
cannot be the celebrity (everyone must know the celebrity)
This elimination property allows us to find a potential celebrity candidate efficiently. We can start with person 0
as our initial candidate and compare them with each subsequent person. Each comparison eliminates exactly one person from consideration.
For example, if we're comparing candidate 0
with person 1
:
- If
knows(0, 1)
returnstrue
, then0
cannot be the celebrity, so we update our candidate to1
- If
knows(0, 1)
returnsfalse
, then1
cannot be the celebrity, so we keep0
as our candidate
By doing this for all n
people, we'll be left with exactly one candidate who hasn't been eliminated. This takes only n - 1
calls to the knows
function.
However, this candidate might not actually be a celebrity - they're just the only person who wasn't eliminated. We need to verify two conditions:
- The candidate doesn't know anyone else (check if
knows(candidate, i)
isfalse
for all other people) - Everyone else knows the candidate (check if
knows(i, candidate)
istrue
for all other people)
This verification step requires at most 2(n - 1)
additional calls to knows
, giving us a total of O(n)
calls, which is optimal since we need to verify relationships with all n
people to confirm a celebrity exists.
Solution Approach
The solution implements a two-phase algorithm:
Phase 1: Find the Celebrity Candidate
We start by assuming person 0
is our potential celebrity candidate (ans = 0
). Then we iterate through persons 1
to n-1
:
ans = 0
for i in range(1, n):
if knows(ans, i):
ans = i
At each step, we check if our current candidate ans
knows person i
:
- If
knows(ans, i)
returnstrue
, thenans
cannot be the celebrity (celebrities don't know anyone), so we updateans = i
- If
knows(ans, i)
returnsfalse
, theni
cannot be the celebrity (everyone must know the celebrity), so we keepans
unchanged
After this loop, ans
holds the only person who could potentially be the celebrity. This takes exactly n-1
calls to the knows
function.
Phase 2: Verify the Candidate
Now we need to verify that our candidate ans
is actually a celebrity by checking two conditions:
for i in range(n):
if ans != i:
if knows(ans, i) or not knows(i, ans):
return -1
return ans
For every other person i
(where i != ans
):
- Check if
knows(ans, i)
istrue
- if yes,ans
knows someone and cannot be a celebrity - Check if
knows(i, ans)
isfalse
- if yes, someone doesn't knowans
and they cannot be a celebrity
If either condition fails, we immediately return -1
indicating no celebrity exists. If we successfully verify all relationships, we return ans
as the celebrity.
The verification phase uses at most 2(n-1)
calls to knows
(two checks for each of the other n-1
people).
Time Complexity: O(n)
- we make at most 3n-3
calls to the knows
function
Space Complexity: O(1)
- we only use a constant amount of extra space
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 n = 4
people (labeled 0, 1, 2, 3) where person 2 is the celebrity.
The relationship matrix looks like this (where 1 means "knows" and 0 means "doesn't know"):
0 1 2 3 0 [0, 0, 1, 0] // Person 0 knows person 2 1 [0, 0, 1, 0] // Person 1 knows person 2 2 [0, 0, 0, 0] // Person 2 knows nobody (celebrity) 3 [0, 0, 1, 0] // Person 3 knows person 2
Phase 1: Find the Celebrity Candidate
Start with ans = 0
as our initial candidate.
-
i = 1: Check
knows(0, 1)
β returnsfalse
(person 0 doesn't know person 1)- Since 0 doesn't know 1, person 1 cannot be the celebrity
- Keep
ans = 0
-
i = 2: Check
knows(0, 2)
β returnstrue
(person 0 knows person 2)- Since 0 knows 2, person 0 cannot be the celebrity
- Update
ans = 2
-
i = 3: Check
knows(2, 3)
β returnsfalse
(person 2 doesn't know person 3)- Since 2 doesn't know 3, person 3 cannot be the celebrity
- Keep
ans = 2
After Phase 1, our candidate is person 2. We made 3 calls to knows
.
Phase 2: Verify the Candidate
Now verify that person 2 is actually the celebrity:
-
i = 0:
- Check
knows(2, 0)
β returnsfalse
β (celebrity shouldn't know anyone) - Check
knows(0, 2)
β returnstrue
β (everyone should know celebrity)
- Check
-
i = 1:
- Check
knows(2, 1)
β returnsfalse
β - Check
knows(1, 2)
β returnstrue
β
- Check
-
i = 3:
- Check
knows(2, 3)
β returnsfalse
β - Check
knows(3, 2)
β returnstrue
β
- Check
All checks pass! Person 2 is confirmed as the celebrity. We made 6 additional calls in Phase 2.
Total calls to knows
: 9 (which is 3n - 3 for n = 4)
The algorithm correctly identified person 2 as the celebrity using O(n) calls to the knows
function.
Solution Implementation
1# The knows API is already defined for you.
2# return a bool, whether a knows b
3# def knows(a: int, b: int) -> bool:
4
5
6class Solution:
7 def findCelebrity(self, n: int) -> int:
8 """
9 Find the celebrity among n people (labeled from 0 to n-1).
10 A celebrity is defined as someone who:
11 1. Is known by everyone else
12 2. Knows nobody else
13
14 Args:
15 n: Total number of people
16
17 Returns:
18 The label of the celebrity if exists, otherwise -1
19 """
20 # Step 1: Find a candidate for celebrity
21 # If current candidate knows person i, then candidate cannot be a celebrity
22 # Person i becomes the new candidate
23 candidate = 0
24 for i in range(1, n):
25 if knows(candidate, i):
26 candidate = i
27
28 # Step 2: Verify if the candidate is actually a celebrity
29 # Check two conditions:
30 # 1. Celebrity should not know anyone (except themselves)
31 # 2. Everyone should know the celebrity
32 for i in range(n):
33 if candidate != i:
34 # If candidate knows person i, or person i doesn't know candidate
35 # then candidate is not a celebrity
36 if knows(candidate, i) or not knows(i, candidate):
37 return -1
38
39 return candidate
40
1/* The knows API is defined in the parent class Relation.
2 boolean knows(int a, int b); */
3
4public class Solution extends Relation {
5 /**
6 * Finds the celebrity in a group of n people.
7 * A celebrity is defined as someone who:
8 * 1. Knows no one else
9 * 2. Is known by everyone else
10 *
11 * @param n The number of people in the group (labeled from 0 to n-1)
12 * @return The label of the celebrity if exists, otherwise -1
13 */
14 public int findCelebrity(int n) {
15 // Phase 1: Find the potential celebrity candidate
16 // Key insight: If A knows B, then A cannot be a celebrity
17 // If A doesn't know B, then B cannot be a celebrity
18 int candidate = 0;
19
20 for (int i = 1; i < n; ++i) {
21 // If current candidate knows person i,
22 // then candidate cannot be a celebrity
23 // Person i becomes the new candidate
24 if (knows(candidate, i)) {
25 candidate = i;
26 }
27 }
28
29 // Phase 2: Verify if the candidate is actually a celebrity
30 // Check two conditions for all other people:
31 // 1. Celebrity should not know anyone
32 // 2. Everyone should know the celebrity
33 for (int i = 0; i < n; ++i) {
34 // Skip checking the candidate against themselves
35 if (candidate != i) {
36 // If candidate knows person i, they're not a celebrity
37 // If person i doesn't know candidate, candidate is not a celebrity
38 if (knows(candidate, i) || !knows(i, candidate)) {
39 return -1;
40 }
41 }
42 }
43
44 // Candidate passed all checks, they are the celebrity
45 return candidate;
46 }
47}
48
1/* The knows API is defined for you.
2 bool knows(int a, int b); */
3
4class Solution {
5public:
6 int findCelebrity(int n) {
7 // Step 1: Find the potential celebrity candidate
8 // Key insight: If person A knows person B, then A cannot be a celebrity
9 // If person A doesn't know person B, then B cannot be a celebrity
10 int candidate = 0;
11 for (int i = 1; i < n; ++i) {
12 if (knows(candidate, i)) {
13 // Current candidate knows person i, so candidate cannot be a celebrity
14 // Person i becomes the new candidate
15 candidate = i;
16 }
17 // If candidate doesn't know i, then i cannot be a celebrity
18 // So we keep the current candidate
19 }
20
21 // Step 2: Verify if the candidate is actually a celebrity
22 // A celebrity must satisfy two conditions:
23 // 1. The celebrity doesn't know anyone
24 // 2. Everyone knows the celebrity
25 for (int i = 0; i < n; ++i) {
26 if (candidate != i) {
27 // Check if candidate knows person i (celebrity shouldn't know anyone)
28 // OR if person i doesn't know the candidate (everyone should know celebrity)
29 if (knows(candidate, i) || !knows(i, candidate)) {
30 return -1; // No celebrity exists
31 }
32 }
33 }
34
35 // The candidate passed all checks, so they are the celebrity
36 return candidate;
37 }
38};
39
1/* The knows API is defined for you.
2 knows(a: number, b: number): boolean */
3
4declare function knows(a: number, b: number): boolean;
5
6function findCelebrity(n: number): number {
7 // Step 1: Find the potential celebrity candidate
8 // Key insight: If person A knows person B, then A cannot be a celebrity
9 // If person A doesn't know person B, then B cannot be a celebrity
10 let candidate = 0;
11
12 for (let i = 1; i < n; i++) {
13 if (knows(candidate, i)) {
14 // Current candidate knows person i, so candidate cannot be a celebrity
15 // Person i becomes the new candidate
16 candidate = i;
17 }
18 // If candidate doesn't know i, then i cannot be a celebrity
19 // So we keep the current candidate
20 }
21
22 // Step 2: Verify if the candidate is actually a celebrity
23 // A celebrity must satisfy two conditions:
24 // 1. The celebrity doesn't know anyone
25 // 2. Everyone knows the celebrity
26 for (let i = 0; i < n; i++) {
27 if (candidate !== i) {
28 // Check if candidate knows person i (celebrity shouldn't know anyone)
29 // OR if person i doesn't know the candidate (everyone should know celebrity)
30 if (knows(candidate, i) || !knows(i, candidate)) {
31 return -1; // No celebrity exists
32 }
33 }
34 }
35
36 // The candidate passed all checks, so they are the celebrity
37 return candidate;
38}
39
Time and Space Complexity
Time Complexity: O(n)
The algorithm consists of two main phases:
-
First loop (candidate selection): Iterates from index 1 to n-1, making
n-1
calls to theknows()
function. This takesO(n)
time. -
Second loop (verification): Iterates through all n people to verify if the candidate is indeed a celebrity. In the worst case, this loop makes
2(n-1)
calls to theknows()
function:- For each person
i
wherei β ans
, it checks:knows(ans, i)
- to verify the candidate doesn't know person iknows(i, ans)
- to verify person i knows the candidate
- This results in at most
2(n-1)
API calls
- For each person
Total API calls: (n-1) + 2(n-1) = 3n - 3
, which is O(n)
.
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space:
- One variable
ans
to store the candidate index - One loop variable
i
No additional data structures are used that scale with the input size n
, resulting in constant space complexity.
Common Pitfalls
Pitfall 1: Incorrect Candidate Elimination Logic
The Problem: A common mistake is misunderstanding why we update the candidate in Phase 1. Some might think:
- "If
knows(candidate, i)
is false, then we should makei
the new candidate" - Or implement redundant checks like also checking
knows(i, candidate)
Why This is Wrong:
The key insight is that with just ONE query knows(candidate, i)
, we can eliminate one person:
- If
knows(candidate, i) == true
: The current candidate CANNOT be a celebrity (celebrities know nobody), so we eliminate them and makei
the new candidate - If
knows(candidate, i) == false
: Personi
CANNOT be a celebrity (everyone must know the celebrity), so we keep the current candidate
Incorrect Implementation:
# WRONG: Unnecessary extra check
candidate = 0
for i in range(1, n):
if knows(candidate, i) and not knows(i, candidate): # Redundant!
candidate = i
Correct Implementation:
# RIGHT: Single check is sufficient
candidate = 0
for i in range(1, n):
if knows(candidate, i):
candidate = i
Pitfall 2: Incomplete Verification
The Problem: After finding a candidate, some solutions forget to verify BOTH conditions:
- The candidate doesn't know anyone else
- Everyone else knows the candidate
Incorrect Implementation:
# WRONG: Only checking one direction
for i in range(n):
if candidate != i:
if not knows(i, candidate): # Only checking if others know candidate
return -1
return candidate
Correct Implementation:
# RIGHT: Check both conditions
for i in range(n):
if candidate != i:
if knows(candidate, i) or not knows(i, candidate):
return -1
return candidate
Pitfall 3: Off-by-One Errors in Loop Ranges
The Problem: Using incorrect loop ranges, especially starting from 0 when we should start from 1, or forgetting to exclude the candidate during verification.
Incorrect Implementation:
# WRONG: Starting from 0 unnecessarily in Phase 1
candidate = 0
for i in range(0, n): # Should start from 1, not 0
if knows(candidate, i):
candidate = i
Why This Matters:
Starting from 0 would make an unnecessary knows(0, 0)
call and could potentially cause logic errors depending on how self-knowledge is defined in the problem.
Pitfall 4: Not Understanding the Optimization
The Problem: Some might try to "optimize" by breaking early or adding unnecessary conditions, not realizing the algorithm is already optimal.
Incorrect Attempt:
# WRONG: Trying to "optimize" with early termination
candidate = 0
found = False
for i in range(1, n):
if knows(candidate, i):
candidate = i
else:
found = True # This doesn't mean we found a celebrity!
break
Why This is Wrong: The Phase 1 loop MUST check all n-1 people to ensure we have the only possible candidate. Early termination would miss potential candidates.
Key Takeaway
The elegance of this solution lies in its simplicity: Phase 1 uses exactly n-1 queries to find the ONLY possible candidate, and Phase 2 verifies this candidate with at most 2(n-1) additional queries. Trying to "improve" this often leads to incorrect solutions or unnecessary complexity.
In a binary min heap, the minimum element can be found in:
Recommended Readings
https assets algo monster cover_photos graph svg Graph Fundamentals Tree with 0 cycle At this point you should be pretty familiar with trees A tree is a special kind of graph a connected acyclic cycle less graph A graph may contain cycle s and nodes could be disconnected A tree
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
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
Want a Structured Path to Master System Design Too? Donβt Miss This!