给定一个房间,有n个人。判断是否存在这样一个人,房间内的所有其他人都认识他,但是他不认识其他的所有人。给定API:know(i, j), 如果为true的话,就表示i认识j,如果为false的话就表示i不认识j。
思路:
当know(i, j)值为true的时候,表示i认识j,则i肯定不是明星;
当know(i, j)值为false的时候,表示i不认识j,则j肯定不是明星。
因此每调用一次know(i, j)的值,我们就可以判断出,其中有一个人不是明星。最多调用n次就可以得到结果,时间复杂度为O(n)。当最后只剩下一个人的时候,不代表这个人一定是明星,通过上述方法,只能保证排除掉的不是明星,但不能保证,剩下的就是明星。所以最后还要遍历一次数组,确认最终剩下的人是否是明星。
方法1:建立一个哈希表将0~n-1存入表中,每当排除一个人不是明星,我们就将这个元素从表中删掉,直到最后表中剩下一个元素。
int hasStar(int n) {
unordered_set<int>s;
for(int i=0; i<n; ++i){
s.insert(i);
}
while(s.size()>1){
for(auto it1=s.begin(); it1!=s.end(); ++it1){
for(auto it2=++s.begin(); it2!=s.end(); ++it2){
if(know(*(it1), *(it2)){
s.erase(it1);
break;
}
else{
s.erase(it2);
}
}
}
}
int flag=(*s.begin());
for(int i=0; i<n; ++i){
if(i==flag) continue;
if(!know(i, flag) || know(flag, i))
return -1;
}
return flag;
}
方法2:用标记数组代替哈希表来写。
int hasStar(int n)
{
vector<int> flag(n, 1);
for (int i = 0; i < n; ++i){
if (flag[i] == 0) continue;
for (int j = i + 1; j < n; ++j){
if (know(i,j)){
flag[i] = 0;
break;
}
else{
flag[j] = 0;
}
}
}
//最后一定会剩下一个元素,但是剩下的不一定是明星
for (int i = 0; i < n; ++i){
if (flag[i] == 1){
for (int j = 0; j < n; ++j){
if(j==i) continue;
if (know(i,j) || !know(j,i))
return -1;
}
return i;
}
}
}
方法3:将以上两种方法优化一下,其实我们不需要哈希表,也不需要flag标记数组。第一步的目的,只是想找出可能是明星的那个人,只需要扫一遍就可以了,不需要标记。
int hasStar(int n) {
int i=0;
for(int j=1; j<n; ++j){
if(know(i,j)){
i=j;
}
}
//最后剩下i,但是i不一定是明星
for(int j=0; j<n; ++j){
if(j==i) continue;
if(know(i,j) || !know(j,i))
return -1;
}
return i;
}