面试题29:数组中出现次数超过一半的数字
思路一:
如果这个数组是已经排序了的数组,那么只要遍历一次就统计出每个数字出现的次数,这样就能找出符合要求的数字了。
题目给出的数组没有说是排好序的,因此需要给它排序。排序的时间复杂度是O(nlogn),再加上遍历的时间复杂度O(n),因此总的复杂度是O(nlogn)。
思路二:
思路一的时间主要是花在排序上。可以创建一个哈希表来消除排序的时间。
哈希表的键值(Key)为数组中的数字,值(Value)为该数字对应的次数。有了这个辅助的哈希表之后,我们只需要遍历数组中的每个数字,找到它在哈希表中对应的位置并增加它出现的次数。
不过本题并没有限制数组里数字的范围,我们要么需要创建一个很大的哈希表,要么需要设计一个很复杂的方法来计算哈希值。因此总体说来这个方法还不是很好。
思路三:
前面两种思路都没有考虑到题目中数组的特性:数组中有个数字出现的次数超过了数组长度的一半。也就是说,有个数字出现的次数比其他所有数字出现次数的和还要多。
因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。
int find(int *a, const int n) {
if(a == NULL || n <= 0) {
return -1;
}
int times = 1;
int ret = a[0];
for(int i = 1; i < n; i ++) {
if(times == 0) {
ret = a[i];
times = 1;
}
if(a[i] == ret) {
times ++;
} else {
times --;
}
}
// 检测找到的那个数的个数是否超过总数的一半
times = 0;
for(i = 0; i < n; i ++) {
if(a[i] == ret) {
times ++;
}
}
if(times * 2 <= n) {
return -1;
}
return ret;
}
疑问: 如果times=0, ret等于a[i],设置times = 1,