在一个聚会上,有 n 个人,大家都在打招呼、聊天、认识新朋友。
这时候主持人说:“在场可能有一位神秘名人! 他的特点是:所有人都认识他,但他一个人也不认识! ”
于是我们就有了今天的主角题
题目背景
你有一个函数 knows(a, b):
- 如果
a知道b,返回true; - 否则返回
false。
你的任务是:在 n 个人中,找出那个名人(celebrity),如果没有,返回 -1。
🧠 名人定义总结一下:
名人是:
- 被所有人认识(in-degree = n - 1)
- 不认识任何人(out-degree = 0)
举个例子
n = 3;
knows(0, 1) = true
knows(1, 2) = true
knows(2, 1) = false
这个图中:
- 0 → 1 ✅
- 1 → 2 ✅
- 2 不认识 1 ❌
分析一下:
- 0 认识别人,不合格 ❌
- 1 被 0 和 2 认识 ✅,但自己认识了 2 ❌
- 2 只被 1 认识 ❌
所以:没有名人,输出 -1。
🚀 思路分析:如何高效找到名人?
可以用逻辑排除法(剪枝)来解决。
- 先挑出候选人:我们用一轮循环判断,如果 candidate 认识 i,就淘汰他,换 i 做候选人;
- 验证候选人:是否满足“不认识任何人”+“被所有人认识”。
代码示意(O(n) 解法)
public class Solution extends Relation {
public int findCelebrity(int n) {
int candidate = 0;
// Step 1: 找出唯一可能的候选人
for (int i = 1; i < n; i++) {
if (knows(candidate, i)) {
candidate = i;
}
}
// Step 2: 验证他是不是真的名人
for (int i = 0; i < n; i++) {
if (i != candidate) {
if (knows(candidate, i) || !knows(i, candidate)) {
return -1;
}
}
}
return candidate;
}
}
理解
- 先假设第 0 个人是名人;
- 从第 1 个人开始,如果当前候选人
candidate认识了别人,他就不配做名人,把他换掉; - 最后得到一个可能是名人的人,验证这个人是不是 不认识任何人 且 被所有人认识。
这就像:
把聚会的人一一对峙,谁“抬头看了别人”就淘汰;最后剩的再验验他是不是被大家都“仰望”。
📌 总结
这道题像是“狼人杀”里的验人过程:
- 谁主动去了解别人(认识别人)就不是好人(名人);
- 真正的名人,是那个“站在角落里默默发光,被所有人记住,但他自己没关注任何人”的角色。