题目:寻找一个ID列表中,有一个ID超过了总数的一半,找出这个ID
分析:
可以对ID进行排序,因为需要寻找的ID超过了一半,所以该ID列表ID[N]中,ID[N/2]一定是这个ID值。复杂度为O(NlgN+1),如果用排序这种方法解决的话,复杂度应该就维持在这个水平,不会降低。再思考一下,难道一定需要排序吗,针对这个问题,我们只是想找出ID数量超过一半的那个唯一的ID。如果每次删除两个不同的ID,那么数量超过一半的那个ID在剩下的ID列表中总会维持超过一半的数量,最后剩下的就一定是该ID。
/*寻找发帖水王,算法有些类似于寻找最大和子序列*/
Type FInd(Type * ID, int N){
Type candicate;
int i, nTimes; //nTimes用来记录重复次数
for(i = nTimes = 0; i < N; i ++){
if(nTimes == 0){
candicate = ID[i]; //如果发现nTimes=0,则前面的重复完全抵消,重新选择一个candicate
nTimes = 1;
}else {
if(candicate == ID[i]) nTimes ++; //如果发现相同ID,重复次数加1
else nTimes --; //否则减少
}
}
return candicate;
}
扩展问题:
“超级水王”没有了,有3个ID发帖也很多,每个人都超过帖子总数的1/4了,怎么快速找到这三个ID?
思路和“超级水王”的是一样的,只是这次需要有三个candicate[3],和三个nTimes[3]。如果找到与这三个ID相同的,则对应的加1,如果都不相同,则三个ID数量都要减1
/*寻找发帖水王,算法有些类似于寻找最大和子序列*/
Type *Find(Type * ID, int N){
Type candicate[3];
int i, j, k, nTimes[3]; //nTimes用来记录重复次数
for(i = 0; i < 3; i++) nTimes[i] = 0;
for(i = 0; i < N; i ++){
for(j = 0; j < 3; j ++){
if(nTimes[j] == 0){
candicate[j] = ID[i]; //如果发现nTimes=0,则前面的重复完全抵消,重新选择一个candicate
nTimes[j] = 1;
}else {
if(candicate[j] == ID[i]) {
nTimes[j] ++; //如果发现相同ID,重复次数加1
break;
}
}
}
//与这三个ID都不同,则都要减1
if(j == 3){
for(k = 0; k < 3; k++) nTimes[k]--;
}
}
return candicate;
}