🕵️‍♀️《谁是聚会里的名人?》——一道好玩的图论逻辑题

90 阅读2分钟

在一个聚会上,有 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

🚀 思路分析:如何高效找到名人?

可以用逻辑排除法(剪枝)来解决。

  1. 先挑出候选人:我们用一轮循环判断,如果 candidate 认识 i,就淘汰他,换 i 做候选人;
  2. 验证候选人:是否满足“不认识任何人”+“被所有人认识”。

代码示意(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 认识了别人,他就不配做名人,把他换掉;
  • 最后得到一个可能是名人的人,验证这个人是不是 不认识任何人被所有人认识

这就像:

把聚会的人一一对峙,谁“抬头看了别人”就淘汰;最后剩的再验验他是不是被大家都“仰望”。

📌 总结

这道题像是“狼人杀”里的验人过程:

  • 谁主动去了解别人(认识别人)就不是好人(名人);
  • 真正的名人,是那个“站在角落里默默发光,被所有人记住,但他自己没关注任何人”的角色。