给定一个数组,其中有一个元素的出现次数超过1/2,如何快速的找出这个元素。这个问题在《编程之美》中是这样描述的:
“研究院的员工和实习生们都很喜欢在Tango上面交流灌水。传说,Tango有一大“水王”,他不但喜欢发贴,还会回复其他ID发的每个帖子。坊间风闻该“水王”发帖数目超过了帖子总数的一半。如果你有一个当前论坛上所有帖子(包括回帖)的列表,其中帖子作者的ID也在表中,你能快速找出这个传说中的Tango水王吗?”
两个问题殊途同归,都是快速查找数组中出现次数超过一半的元素。
最直接的解法:对数组进行排序,取n/2 + 1个元素即为所求,这种算法的复杂度最坏的情况下是O(n^2),平均情况下是O(n*log2n)。
另一种方式:可以根据不同就抵消,相同就增一的计算器完成。设置一个当前值和当前值的计数器,初始化当前值为数组首元素,计数器值为1,然后从第二个元素开始遍历整个数组,对于每个被遍历到的值a[i]
1 如果a[i]==currentValue,则计数器值加1
2 如果a[i] != currentValue, 则计数器值减1,如果计数器值小于0,则更新当前值为a[i],并将计数器值重置为1
#include <stdio.h>
#include <stdlib.h>
int FindMoreValue(int arr[], int n){
int i;
int curr_count = 1;
int curr_value = arr[0];
for(i = 1; i < n; i++){
if(arr[i] == curr_value){
curr_count++;
}else{
curr_count--;
if(curr_count < 0){
curr_value = arr[i];
curr_count = 1;
}
}
}
if(curr_count > 0){
return curr_value;
}else{
//不存在出现次数超过一半的元素
return -1;
}
}
int main(int argc, char * argv[]){
int arr[10] = {1,1,1,1,1,1,2,3,4,5};
int value = FindMoreValue(arr, 10);
printf("Find Value:%d\n", value);
}
如果要查找数组中出现次数超过1/3的两个元素呢?
#define NAN (0.0 / 0.0)
int FindMore(int arr[], int n, int cans[2]){
int i;
if(n < 2){
return -1;
}
int times[2];
times[0] = times[1] = 0;
cans[0] = cans[1] = NAN;
for(i = 0; i < n; i++){
if(arr[i] == cans[0]){
times[0]++;
}else if(arr[i] == cans[1]){
times[1]++;
}else{
times[0]--;
times[1]--;
if(times[0] < 0){
cans[0] = arr[i];
times[0] = 1;
}else if(times[1] < 0){
cans[1] = arr[i];
times[1] = 1;
}
}
}
return 0;
}
以此类推,求出现次数超过1/4的三个元素等等。