1. 程式人生 > >#leetcoce#277. Find the Celebrity

#leetcoce#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

.

--------------------------------------------------------------------------------------------------------------------

由題目秒數得知,celebrity如下性質: 

  1. 其他所有人都認識 celebrity, 如下圖 藍色的 T, 表示 true
  2. celebrity 不認識其他所有人, 如下圖紅色的 F, 表示 false

題目給的 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<Integer> 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;
    }        
}