> For the complete documentation index, see [llms.txt](https://cai-sen-se.gitbook.io/leetcode/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cai-sen-se.gitbook.io/leetcode/201-400/277.-sou-xun-ming-ren.md).

# 277.搜寻名人

[原题](https://leetcode-cn.com/problems/find-the-celebrity/)

## 一、模拟

两人之间关系只有四种：你认识我我不认识你，我认识你你不认识我，咱俩互相认识，咱两互相不认识。 对于情况一，cand 认识 other，所以 cand 肯定不是名人，排除。因为名人不可能认识别人。

对于情况二，other 认识 cand，所以 other 肯定不是名人，排除。

对于情况三，他俩互相认识，肯定都不是名人，可以随便排除一个。

对于情况四，他俩互不认识，肯定都不是名人，可以随便排除一个。因为名人应该被所有其他人认识。

```python
# The knows API is already defined for you.
# return a bool, whether a knows b
# def knows(a: int, b: int) -> bool:

class Solution:
    def findCelebrity(self, n: int) -> int:
        q = []
        # 所有人入队
        for i in range(n):
            q.append(i)
        while len(q) >= 2:
            cand = q.pop()
            other = q.pop()
            if knows(cand, other) or not knows(other, cand):
                # cand不可能是名人，让other归队
                q.append(other)
            else:
                # other不可能是名人，让cand归队
                q.append(cand)
        # 只剩最后一个候选人，判断其是否为名人
        cand = q.pop()
        for i in range(n):
            if i == cand:   #跳过自身
                continue
            # 即看是否全部人都认识他，且他不认识任何人。有一个反面则-1
            if not (knows(i, cand) and not knows(cand, i)):
                return -1
        return cand
```

每轮while排除一个候选人，最坏情况执行n轮，时间复杂度O(n)，空间O(n)。

队列只是用于暂存候选人，每次找两个来比较和淘汰，因此归队的顺序无所谓，插头或插尾都可

## 改进

不用再两两比较，先假设名人为0，然后与other=1\~n-1进行比较判断：1、若cand不是名人，则假设为other；2、若other不是名人，则暂时假设cand为名人，other换下一位。

空间复杂度优化为O(1)

```python
class Solution:
    def findCelebrity(self, n: int) -> int:
        # 先假设cand是名人
        cand = 0
        other = 1
        while other < n:
            if knows(cand, other) or not knows(other, cand):
                # cand不可能是名人，排除
                # 假设other是名人
                cand = other
            # else:
                # other 不可能是名人，排除
                # 什么都不用做，继续假设 cand 是名人
            other += 1
        # 只剩最后一个候选人，判断其是否为名人
        for i in range(n):
            if i == cand:   #跳过自身
                continue
            # 即看是否全部人都认识他，且他不认识任何人。有一个反面则-1
            if not (knows(i, cand) and not knows(cand, i)):
                return -1
        return cand
```

## 二、图（超时）

`i`认识`j`，即`knows(i,j)返回true`，可以理解为一条i指向j的边，此时i出度加一，j入度加一。遍历所有节点两两knows关系，得到一个有向图，则若存在名人，他的入度为n-1， 出度为0

该方法虽然简单直接，但是调用太多次knows，因此效率不如方法一

```python
class Solution:
    def findCelebrity(self, n: int) -> int:
        inDeg = [0] * n     # 所有节点入度
        outDeg = [0] * n    # 所有节点出度
        for i in range(n):
            for j in range(i):
                if knows(i, j):
                    outDeg[i] += 1
                    inDeg[j] += 1
                if knows(j, i):
                    outDeg[j] += 1
                    inDeg[i] += 1
        for k in range(n):
            if inDeg[k] == n-1 and outDeg[k] == 0:
                return k
        return -1
```
