LeetCode 277: Find the Celebrity (Candidate Elimination + Verification)
LeetCode 277Two PointersGraphToday we solve LeetCode 277 - Find the Celebrity.
Source: https://leetcode.com/problems/find-the-celebrity/
English
Problem Summary
There are n people labeled 0..n-1. A celebrity is known by everyone else and knows nobody. You can only call knows(a, b). Return the celebrity index, or -1 if none exists.
Key Insight
In one pass, we can eliminate impossible candidates. If knows(cand, i) is true, cand cannot be celebrity, so set cand = i. Otherwise i cannot be celebrity. After this pass, only one candidate remains, then verify it.
Algorithm
- Start cand = 0.
- For i = 1..n-1: if knows(cand, i), update cand = i.
- Verify candidate: for every j != cand, require knows(cand, j) == false and knows(j, cand) == true.
- If any check fails return -1, otherwise return cand.
Complexity Analysis
Time: O(n) calls to knows (about 3n).
Space: O(1).
Common Pitfalls
- Doing only elimination but skipping final verification.
- Checking only one condition in verification.
- Forgetting to skip j == cand.
Reference Implementations (Java / Go / C++ / Python / JavaScript)
/* The knows API is defined in the parent class Relation.
boolean knows(int a, int b); */
public class Solution extends Relation {
public int findCelebrity(int n) {
int cand = 0;
for (int i = 1; i < n; i++) {
if (knows(cand, i)) {
cand = i;
}
}
for (int j = 0; j < n; j++) {
if (j == cand) continue;
if (knows(cand, j) || !knows(j, cand)) {
return -1;
}
}
return cand;
}
}/* The knows API is defined for you.
func knows(a int, b int) bool */
func findCelebrity(n int) int {
cand := 0
for i := 1; i < n; i++ {
if knows(cand, i) {
cand = i
}
}
for j := 0; j < n; j++ {
if j == cand {
continue
}
if knows(cand, j) || !knows(j, cand) {
return -1
}
}
return cand
}/* The knows API is defined for you.
bool knows(int a, int b); */
class Solution {
public:
int findCelebrity(int n) {
int cand = 0;
for (int i = 1; i < n; ++i) {
if (knows(cand, i)) {
cand = i;
}
}
for (int j = 0; j < n; ++j) {
if (j == cand) continue;
if (knows(cand, j) || !knows(j, cand)) {
return -1;
}
}
return cand;
}
};# The knows API is already defined for you.
# def knows(a: int, b: int) -> bool:
class Solution:
def findCelebrity(self, n: int) -> int:
cand = 0
for i in range(1, n):
if knows(cand, i):
cand = i
for j in range(n):
if j == cand:
continue
if knows(cand, j) or not knows(j, cand):
return -1
return cand/**
* @param {number} n
* @return {number}
*/
var solution = function(knows) {
return function(n) {
let cand = 0;
for (let i = 1; i < n; i++) {
if (knows(cand, i)) {
cand = i;
}
}
for (let j = 0; j < n; j++) {
if (j === cand) continue;
if (knows(cand, j) || !knows(j, cand)) {
return -1;
}
}
return cand;
};
};中文
题目概述
有 n 个人,编号 0..n-1。名人满足:所有其他人都认识他,并且他谁也不认识。你只能通过 knows(a, b) 查询关系。返回名人编号,不存在则返回 -1。
核心思路
先做候选人淘汰。若 knows(cand, i) 为真,说明 cand 不可能是名人,候选改成 i;否则 i 不可能是名人。一次遍历后只剩一个候选,再做严格验真。
算法步骤
- 初始化 cand = 0。
- 遍历 i = 1..n-1:若 knows(cand, i),则 cand = i。
- 对每个 j != cand 校验两条:knows(cand, j) 必须为假,knows(j, cand) 必须为真。
- 任一条件失败返回 -1,全部通过返回 cand。
复杂度分析
时间复杂度:O(n)(knows 调用数量约为 3n)。
空间复杂度:O(1)。
常见陷阱
- 只做候选淘汰,不做最终验真。
- 验证时只检查“别人认识候选”或只检查“候选不认识别人”其中一条。
- 忘记跳过 j == cand。
多语言参考实现(Java / Go / C++ / Python / JavaScript)
/* The knows API is defined in the parent class Relation.
boolean knows(int a, int b); */
public class Solution extends Relation {
public int findCelebrity(int n) {
int cand = 0;
for (int i = 1; i < n; i++) {
if (knows(cand, i)) {
cand = i;
}
}
for (int j = 0; j < n; j++) {
if (j == cand) continue;
if (knows(cand, j) || !knows(j, cand)) {
return -1;
}
}
return cand;
}
}/* The knows API is defined for you.
func knows(a int, b int) bool */
func findCelebrity(n int) int {
cand := 0
for i := 1; i < n; i++ {
if knows(cand, i) {
cand = i
}
}
for j := 0; j < n; j++ {
if j == cand {
continue
}
if knows(cand, j) || !knows(j, cand) {
return -1
}
}
return cand
}/* The knows API is defined for you.
bool knows(int a, int b); */
class Solution {
public:
int findCelebrity(int n) {
int cand = 0;
for (int i = 1; i < n; ++i) {
if (knows(cand, i)) {
cand = i;
}
}
for (int j = 0; j < n; ++j) {
if (j == cand) continue;
if (knows(cand, j) || !knows(j, cand)) {
return -1;
}
}
return cand;
}
};# The knows API is already defined for you.
# def knows(a: int, b: int) -> bool:
class Solution:
def findCelebrity(self, n: int) -> int:
cand = 0
for i in range(1, n):
if knows(cand, i):
cand = i
for j in range(n):
if j == cand:
continue
if knows(cand, j) or not knows(j, cand):
return -1
return cand/**
* @param {number} n
* @return {number}
*/
var solution = function(knows) {
return function(n) {
let cand = 0;
for (let i = 1; i < n; i++) {
if (knows(cand, i)) {
cand = i;
}
}
for (let j = 0; j < n; j++) {
if (j === cand) continue;
if (knows(cand, j) || !knows(j, cand)) {
return -1;
}
}
return cand;
};
};
Comments