本文转自:http://zhedahht.blog.163.com/blog/static/25411174201085114733349/
题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。
分析:这是一道广为流传的面试题,包括百度、微软和Google在内的多家公司都曾经采用过这个题目。要几十分钟的时间里很好地解答这道题,除了较好的编程能力之外,还需要较快的反应和较强的逻辑思维能力。
看到这道题,我们马上就会想到,要是这个数组是排序的数组就好了。如果是排序的数组,那么我们只要遍历一次就可以统计出每个数字出现的次数,这样也就能找出符合要求的数字了。题目给出的数组没有说是排好序的,因此我们需要给它排序。排序的时间复杂度是O(nlogn),再加上遍历的时间复杂度O(n),因此总的复杂度是O(nlogn)。
接下来我们试着看看能不能想出更快的算法。前面思路的时间主要是花在排序上。我们可以创建一个哈希表来消除排序的时间。哈希表的键值(Key)为数组中的数字,值(Value)为该数字对应的次数。有了这个辅助的哈希表之后,我们只需要遍历数组中的每个数字,找到它在哈希表中对应的位置并增加它出现的次数。这种哈希表的方法在数组的所有数字都在一个比较窄的范围内的时候很有效。
前面两种思路都没有考虑到题目中数组的特性:数组中有个数字出现的次数超过了数组长度的一半。也就是说,有个数字出现的次数比其他所有数字出现次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加 1 。如果下一个数字和我们之前保存的数字不同,则次数减 1 。如果次数为零,我们需要保存下一个数字,并把次数设为 1 。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为 1 时对应的数字。
基于这个思路的代码如下:
#include <iostream>
using namespace std;
bool invaluedInput = false;
int TheNumMoreThanHalf(int arr[],int len)
{
if(NULL == arr || len <= 0)
{
invaluedInput = true;
return 0;
}
invaluedInput = false;
int number = arr[0];
int count = 1;
int i = 1;
for(i; i<len; i++)
{
if(0 == count)
{
number = arr[i];
count = 1;
}
else
{
if(arr[i] == number)
count++;
else
count--;
}
}
//找到number后,检查number的出现次数是否多于数组的一半
count = 0;
for(i=0; i<len; i++)
{
if(arr[i] == number)
count++;
}
if(2*count <= len)
{
invaluedInput = true;
return 0;
}
else
return number;
}
int main()
{
int arr[] = {1,2,3,2,2,4,5,2,2,5,3,2,2,2,6};
int len = sizeof(arr)/sizeof(int);
int tmp = TheNumMoreThanHalf(arr,len);
cout<<tmp<<endl;
}