A celebrity is someone who is known by everyone but knows nobody. For each person, we can check if they satisfy both conditions: they don't know anyone else, and everyone else knows them. If both conditions hold, that person is the celebrity.
i from 0 to n-1:i is a celebrity by verifying two conditions for every other person j:i does not know j.j knows i.j, return i.-1.class Solution:
def findCelebrity(self, n: int) -> int:
self.n = n
for i in range(n):
if self.is_celebrity(i):
return i
return -1
def is_celebrity(self, i):
for j in range(self.n):
if i == j: continue # Don't ask if they know themselves.
if knows(i, j) or not knows(j, i):
return False
return TrueWe don't know what time and space the knows(...) API uses. Because it's not our concern, we'll assume it's O(1) for the purpose of analysing our algorithm.
Where is the number of nodes in the graph.
Each knows(a, b) call eliminates one person from being a celebrity. If a knows b, then a cannot be the celebrity (celebrities know nobody). If a doesn't know b, then b cannot be the celebrity (everyone must know the celebrity). By iterating through all people once, we can narrow down to a single candidate. We then verify this candidate with a second pass.
candidate = 0.i from 1 to n-1:candidate knows i, update candidate = i (previous candidate is disqualified).-1.class Solution:
def findCelebrity(self, n: int) -> int:
self.n = n
celebrity_candidate = 0
for i in range(1, n):
if knows(celebrity_candidate, i):
celebrity_candidate = i
if self.is_celebrity(celebrity_candidate):
return celebrity_candidate
return -1
def is_celebrity(self, i):
for j in range(self.n):
if i == j: continue
if knows(i, j) or not knows(j, i):
return False
return TrueWe don't know what time and space the knows(...) API uses. Because it's not our concern, we'll assume it's O(1) for the purpose of analysing our algorithm.
Where is the number of nodes in the graph.
The logical deduction approach may call knows(a, b) multiple times with the same arguments during the verification phase. By caching the results of each call, we can avoid redundant API calls. This is particularly useful when the knows function is expensive to evaluate.
knows(a, b) calls.knows.-1.from functools import lru_cache
class Solution:
@lru_cache(maxsize=None)
def cachedKnows(self, a, b):
return knows(a, b)
def findCelebrity(self, n: int) -> int:
self.n = n
celebrity_candidate = 0
for i in range(1, n):
if self.cachedKnows(celebrity_candidate, i):
celebrity_candidate = i
if self.is_celebrity(celebrity_candidate):
return celebrity_candidate
return -1
def is_celebrity(self, i):
for j in range(self.n):
if i == j: continue
if self.cachedKnows(i, j) or not self.cachedKnows(j, i):
return False
return TrueWe don't know what time and space the knows(...) API uses. Because it's not our concern, we'll assume it's O(1) for the purpose of analysing our algorithm.
Where is the number of nodes in the graph.
After finding a candidate using logical deduction, you must verify that the candidate truly knows nobody and is known by everyone. The candidate elimination only guarantees the candidate could be a celebrity, not that they definitely are one. Skipping verification can return false positives.
In the logical deduction approach, starting from candidate 0 and iterating from index 1 is intentional. Starting the loop from index 0 with knows(candidate, 0) would incorrectly update the candidate when comparing a person to themselves, since knows(0, 0) might return true.
When verifying the candidate, always skip the case where i == j. Asking if someone knows themselves is undefined behavior in this problem and could return unexpected results that break your verification logic.