1. 题目描述 Problem Description

2. 题目解析(参考了Prof. Sarrafzadeh 180讲的内容)

首先题目定义了:如果一个人A是名人,那么其他的n-1个人都认识A,而且A不认识其他的n-1个人。这题的目标是要从n个人中找出其中的名人,如果没有,则告知其中没有名人。我们可以调用knows(a,b)函数来询问a是否认识b,而我们要尽量少调用这个函数。

这题的关键是理解题意,题意其实比较容易理解错,所以有几个小问题可以帮大家理解:

(1)n个人中最多可以有几个名人?

答案:是1个。如果这n个人中有2个名人分别为A和B,那么按照定义,如果一个人A是名人,那么其他的n-1个人都认识A,而且A不认识其他的n-1个人,这也就是说A不认识B。但与此同时我们又定义了如果一个人B是名人,那么其他的n-1个人都认识B,那么A也应该认识B。这就产生了 contradiction了。因此其中不可以有2个或2个以上的名人。

(2) 如果其他n-1个人都认识A,但是A认识了n-1个人中其中一个人,那么A还是名人吗?

答案:不是的。

(3) 如果A不认识其他的n-1个人,但是n-1个人中有人不认识A,那么A还是名人吗?

答案:不是的。

-

这题最直接的想法应该是暴力,但是暴力的复杂度是多少呢?对于每个人我们需要询问:

(1) 他是否认识剩下的n-1个人: 最坏的情况需要调用knows(a,b)函数n-1次

(2) 剩下的n-1个人是否认识他:最坏的情况需要调用knows(a,b)函数n-1次

有的可能会重复,但是总体来说需要询问的次数是$\frac{n(n-1)}{2}$。即时间复杂度为 O($n^2$)

-

有没有办法优化算法?

如果我们从n个人中任意挑两个人a,b出来,询问啊是否认识b,那么只有两种情况:

(1)a认识b:那么a肯定不是名人。

(2)b认识a:那么b肯定不是名人。

所以任何一种情况,我们都可以排除掉2个人中的1一个人。如果我们不断地重复的这个过程,直到只剩下一个人,那么我们会做n-1次对比。而剩下这个人是唯一可能成为名人的人,那么我们需要询问剩下的n-1个人是否认识他,也需要询问他是否认识剩下的n-1个人。

因此我们一共需要询问3(n-1)次——时间复杂度为O($n$)。

3. Python 实现

 # The knows API is already defined for you.
 # @param a, person a
 # @param b, person b
 # @return a boolean, whether a knows b
 # def knows(a, b):

 class Solution(object):
     def findCelebrity(self, n):
         """
         :type n: int
         :rtype: int
         """
         if n == 0:
             return -1
         curr_stay = 0
         for i in range(1, n):
             if knows(curr_stay, i):
                 curr_stay = i
         for i in range(0, n):
             if i == curr_stay:
                 continue
             if knows(curr_stay, i):
                 return -1
             if not knows(i, curr_stay):
                 return -1
         return curr_stay
             
05-02 19:52