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
.
--------------------------------------------------------------------------------------------------------------------
由题目秒数得知,celebrity如下性质:
题目给的 knows(a, b) 如果返回 true,则 a 认识 b, a 不可能是 celebrity,
如果返回 false,则 a 不认识 b, b 不可能是 celebrity, 所以每call 一次 knows()method,总能排除一个人,
最开始想的两层循环,根据上面的性质一一排除,到最后剩一个的时候再按照celebrity的性质判断是否符合
两两比较更有效率, 维护一个Queue,把有可能的人放在queue中比较,最后剩一个的时候就是可能的celebrity。用了一个Boolean[][] 数组来避免重复的 knows() call
0 | 1 | 2 | 3 | 4 | |
0 | F | T | T | T | F |
1 | F | F | T | T | T |
2 | F | F | F | T | T |
3 | F | F | F | F | F |
4 | T | T | T | T | F |
/* The knows API is defined in the parent class Relation.
boolean knows(int a, int b); */
public class Solution extends Relation {
public int findCelebrity(int n) {
Queue queue = new LinkedList<>();
for(int i = 0; i < n; i++){
queue.offer(i);
}
Boolean[][] know = new Boolean[n][n];//default will be null
while(!queue.isEmpty()){
if(queue.size() == 1){
//now there is only one people that might be the celebrity, we need to iterate all other people to see if this guy satisfies he/she does not know any others && all others knows him/her
int cur = queue.poll();
for(int i = 0; i < n; i++){
if(i == cur)
continue;
if(know[cur][i] == null){
if(knows(cur, i)){
return -1;
}
}else if(know[cur][i]){
return -1;
}
if(know[i][cur] == null){
if(!knows(i, cur)){
return -1;
}
}else if(!know[i][cur]){
return -1;
}
}
return cur;
}
int i = queue.poll();
int j = queue.poll();
if(knows(i, j)){//i knows j, i must not be the celebrity, j may be
queue.offer(j);
know[i][j] = true;
}else{//i does not know j, j must not be the celebrity, i may be
queue.offer(i);
know[i][j] = false;
}
}
return -1;
}
}
看了discuss的讨论,发现下面的解法更简洁一些,调用 knows 的次数肯定比维护 Boolean[][]数组多,但是多得也比较有限。
因为每次调用 knows() , 都可以排除掉一个人,所以下面这种方法和维护Queue然后一直比较 有可能成为 celebrity的人的比较次数是同样的,都是 n - 1 次 比较。
/* The knows API is defined in the parent class Relation.
boolean knows(int a, int b); */
public class Solution extends Relation {
public int findCelebrity(int n) {
int candidate = 0;
for(int i = 1; i < n; i++){
if(knows(candidate, i)){
candidate = i;
}
}
// now we already know that for x in [candidate + 1 to n - 1], knows(candidate, x) returns false;
for(int i = 0; i < n; i++){
if(i == candidate){
continue;
}
if(i < candidate && knows(candidate, i)){
return -1;
}
if(!knows(i, candidate)){
return -1;
}
}
return candidate;
}
}