Stacks & Queues · 10 of 38
The celebrity Problem
At a party, find the one guest everybody knows who knows nobody, by asking as few questions as possible.
- Simple way
- time O(n^2)
room O(1) - Clever way
- time O(n)
room O(n)
The problem, simply
There are n people at a party, numbered 0 to n - 1. You get a table M where M[i][j] == 1 means person i knows person j. A celebrity is a person who is known by everyone else and knows nobody.
Find the celebrity and give back their number, or -1 if there is none. There can be at most one: if two people were both celebrities, each would have to know the other, which a celebrity never does.
A story to remember it
Picture it
Every arrow points from someone to a person they know. Person 1 has an arrow coming in from everyone and no arrow going out. That is the celebrity.
- answer
Examples
The party in the animation
- In
M = [[0,1,1,0,1],[0,0,0,0,0],[0,1,0,1,0],[0,1,0,0,0],[0,1,0,1,0]]- Out
1
Row 1 is all 0s, and column 1 has a 1 in every other row.
A classic four person party
- In
M = [[0,0,1,0],[0,0,1,0],[0,0,0,0],[0,0,1,0]]- Out
2
Everyone knows person 2, and person 2 knows nobody.
Two friends
- In
M = [[0,1],[1,0]]- Out
-1
They know each other, so neither one knows nobody.
Almost famous
- In
M = [[0,1,0],[0,0,0],[0,0,0]]- Out
-1
Person 1 knows nobody, but person 2 does not know person 1. Elimination still leaves a last candidate, and only the final check catches this.
Alone at the party
- In
M = [[0]]- Out
0
With one person, "everyone else" is nobody, so that person counts as the celebrity.
Way 1: the simple way
Check every person directly.
- For each person
c: check thatM[c][j] == 0for every otherj(knows nobody). - Check that
M[j][c] == 1for every otherj(known by everyone). - If both hold, return
c. If nobody passes, return-1.
The same party as a table: row i, column j is 1 when i knows j. A celebrity has a row of all 0s and a column of all 1s (except itself). Way 1 checks every row and column.
- answer
- path
class Solution {
public int celebrity(int[][] M) {
int n = M.length;
for (int c = 0; c < n; c++) {
boolean ok = true;
for (int j = 0; j < n && ok; j++) {
if (j == c) continue;
if (M[c][j] == 1 || M[j][c] == 0) ok = false; // knows someone, or someone does not know c
}
if (ok) return c;
}
return -1;
}
}How long it takes: up to n people times n checks, O(n^2). Extra room: O(1).
Way 2: the clever way
Use the knock out rule with a stack.
- Push every person onto a stack.
- While more than one person is on the stack: pop
aandb. Ifaknowsb,ais out, so pushbback. Otherwisebis out, so pushaback. - One candidate
cis left. Check it:cmust know nobody, and everyone else must knowc. If either check fails, return-1.
Each question knocks one person out
01 / 07
Five people. An arrow from a to b means a knows b. Push everyone onto the stack: each one might be the celebrity.
- looking now
- answer
- rejected
- ignored
import java.util.*;
class Solution {
public int celebrity(int[][] M) {
int n = M.length;
if (n == 0) return -1; // nobody at the party
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) stack.push(i); // everyone might be the one
while (stack.size() > 1) {
int a = stack.pop(), b = stack.pop();
if (M[a][b] == 1) stack.push(b); // a knows b: a is out
else stack.push(a); // a does not know b: b is out
}
int c = stack.pop();
for (int j = 0; j < n; j++) { // the last one must pass the full check
if (j == c) continue;
if (M[c][j] == 1 || M[j][c] == 0) return -1;
}
return c;
}
}How long it takes: n - 1 questions to knock people out, then at most 2(n - 1) questions to check, O(n). Extra room: the stack of n people, O(n).
Another way: two pointers, no stack
You do not need the stack. Put one finger on person 0 and one on person n - 1. Ask if the left one knows the right one. If yes, the left one is out, so move the left finger right. If no, the right one is out, so move the right finger left. When the fingers meet, that person is the candidate.
class Solution {
public int celebrity(int[][] M) {
int n = M.length;
if (n == 0) return -1;
int lo = 0, hi = n - 1;
while (lo < hi) {
if (M[lo][hi] == 1) lo++; // lo knows hi: lo is out
else hi--; // lo does not know hi: hi is out
}
int c = lo;
for (int j = 0; j < n; j++) {
if (j == c) continue;
if (M[c][j] == 1 || M[j][c] == 0) return -1;
}
return c;
}
}Same O(n) time, and only O(1) extra room. This is the answer most interviewers hope to hear last.
Watch out for
- Counting
M[c][c]. The diagonal says whether a person "knows themselves", and it must be ignored in both checks. - Asking
M[b][a]when you meantM[a][b]. Write the rule down: "a knows b means a is out". - Thinking there might be two celebrities. There is at most one, so return as soon as the single candidate passes.
What interviewers ask next
- The table is hidden and you only have a function
knows(a, b), which is expensive. How many calls do you need? At most3(n - 1):n - 1to eliminate and2(n - 1)to check. You can save a few by remembering answers you already got during elimination. - Can you prove
O(n)questions are needed? Yes. If any person were never asked about, you could not tell whether they are the celebrity, so every person must be involved in some question. - Graph view: the celebrity is a node with
n - 1arrows coming in and 0 going out. With the table you can find it by in-degree and out-degree, but that reads alln^2cells. - Find the town judge (LeetCode 997): the same idea with a list of "trusts" pairs. Count in-degree minus out-degree and look for
n - 1.
Remember this
Grown-up words: the party is a directed graph, the knows table is an adjacency matrix, the celebrity is a universal sink (in-degree n - 1, out-degree 0), the knock out game is elimination, and the two fingers are the two pointer technique.