名人问题 算法解析与Python 实现 O(n) 复杂度 (以Leetcode 277. Find the Celebrity为例)

时间:2023-03-10 08:11:29
名人问题 算法解析与Python 实现 O(n) 复杂度 (以Leetcode 277. Find the Celebrity为例)

1. 题目描述 Problem Description

Leetcode 277. Find the Celebrity

Suppose you are at a party with n people (labeled from 0 to n - 1) and among them, there may exist one celebrity. The definition of a celebrity is that all the other n - 1 people know him/her but he/she does not know any of them.

Now you want to find out who the celebrity is or verify that there is not one. The only thing you are allowed to do is to ask questions like: "Hi, A. Do you know B?" to get information of whether A knows B. You need to find out the celebrity (or verify there is not one) by asking as few questions as possible (in the asymptotic sense).

You are given a helper function bool knows(a, b) which tells you whether A knows B. Implement a function int findCelebrity(n), your function should minimize the number of calls to knows.

Note: There will be exactly one celebrity if he/she is in the party. Return the celebrity's label if there is a celebrity in the party. If there is no celebrity, return -1.

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