Facebook Pixel

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 to n - 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 knows b, then a cannot be the celebrity (celebrities don't know anyone)
  • If a doesn't know b, then b 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) returns true, then 0 cannot be the celebrity, so we update our candidate to 1
  • If knows(0, 1) returns false, then 1 cannot be the celebrity, so we keep 0 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:

  1. The candidate doesn't know anyone else (check if knows(candidate, i) is false for all other people)
  2. Everyone else knows the candidate (check if knows(i, candidate) is true 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) returns true, then ans cannot be the celebrity (celebrities don't know anyone), so we update ans = i
  • If knows(ans, i) returns false, then i cannot be the celebrity (everyone must know the celebrity), so we keep ans 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) is true - if yes, ans knows someone and cannot be a celebrity
  • Check if knows(i, ans) is false - if yes, someone doesn't know ans 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 Evaluator

Example 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) β†’ returns false (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) β†’ returns true (person 0 knows person 2)

    • Since 0 knows 2, person 0 cannot be the celebrity
    • Update ans = 2
  • i = 3: Check knows(2, 3) β†’ returns false (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) β†’ returns false βœ“ (celebrity shouldn't know anyone)
    • Check knows(0, 2) β†’ returns true βœ“ (everyone should know celebrity)
  • i = 1:

    • Check knows(2, 1) β†’ returns false βœ“
    • Check knows(1, 2) β†’ returns true βœ“
  • i = 3:

    • Check knows(2, 3) β†’ returns false βœ“
    • Check knows(3, 2) β†’ returns true βœ“

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:

  1. First loop (candidate selection): Iterates from index 1 to n-1, making n-1 calls to the knows() function. This takes O(n) time.

  2. 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 the knows() function:

    • For each person i where i β‰  ans, it checks:
      • knows(ans, i) - to verify the candidate doesn't know person i
      • knows(i, ans) - to verify person i knows the candidate
    • This results in at most 2(n-1) API calls

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 make i 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 make i the new candidate
  • If knows(candidate, i) == false: Person i 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:

  1. The candidate doesn't know anyone else
  2. 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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the minimum element can be found in:


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More