数组中超过出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。
分析:编程之美上也有这道题,不过它变换了题目的表述形式,如下:寻找发帖水王:
Tango是微软亚洲研究院的一个试验项目,如图2-1所示。研究院的员工和实习生们都很喜欢在Tango上面交流灌水。传说,Tango有一大“水王”,他不但喜欢发帖,还会回复其他ID发的每个帖子。坊间风闻该“水王”发帖数目超过了帖子总数的一半。如果你有一个当前论坛上所有帖子(包括回帖)的列表,其中帖子作者的ID也在表中,你能快速找出这个传说中的Tango水王吗?
图2-1 Tango
咱们来解决这道题,以微软面试100题第74题的阐述为准(本程序员编程艺术系列就是按照之前整理的微软100题一题一题展开而来的)。
一个数组中有很多数,现在我们要找出这个数组中那个超过出现次数一半的数字,怎么找呢?大凡当我们碰到某一个杂乱无序的东西时,我们人的内心本质期望是希望把它梳理成有序的。所以,我们得分两种情况来讨论,无序和有序:
或许,你还没有明白上述思路4的意思,举个简单的例子吧,如数组a[5]={0,1,2,1,1};
很显然,若我们要找出数组a中出现次数超过一半的数字,这个数字便是1,若根据上述思路4所述的方法来查找,我们应该怎么做呢?通过一次性遍历整个数组,然后每次删除相同的两个数字,过程如下简单表示:
0 1 2 1 1 =>2 1 1=>1,最终,1即为所找。
Ok,思路清楚了,那么接下来,咱们就来写代码实现上述思路4所述的方法:
//改自编程之美 2010
Type Find(Type* a, int N) //a代表数组,N代表数组长度
{
Type candidate;
int nTimes, i;
for(i = nTimes = 0; i < N; i++)
{
if(nTimes == 0)
{
candidate = a[i], nTimes = 1;
}
else
{
if(candidate == a[i])
nTimes++;
else
nTimes--;
}
}
return candidate;
}
咱们再来看一段代码,如下:
// 那么要找的数字肯定是最后一次把次数设为1时对应的数字。
//copyright@zhedahht
//July,updated,
//2011.04.16。
#include <iostream>
using namespace std;
bool g_Input = false;
int Num(int* numbers, unsigned int length)
{
if(numbers == NULL && length == 0)
{
g_Input = true;
return 0;
}
g_Input = false;
int result = numbers[0];
int times = 1;
for(int i = 1; i < length; ++i)
{
if(numbers[i] == result)
times++;
else
times--;
if(times == 0)
{
result = numbers[i];
times = 1;
}
}
//检测输入是否有效。
times = 0;
for(i = 0; i < length; ++i)
{
if(numbers[i] == result)
times++;
}
if(times * 2 <= length)
//检测的标准是:如果数组中并不包含这么一个数字,那么输入将是无效的。
{
g_Input = true;
result = 0;
}
return result;
}
int main()
{
int a[10]={1,2,3,4,6,6,6,6,6};
int* n=a;
cout<<Num(a,9)<<endl;
return 0;
}
这段代码与上段代码本质上并无二致,不过有几个问题,还是需要我们注意: