您的位置:首页 > 其它

18数组中出现次数超过一半的数

2015-06-12 08:39 489 查看
题目描述:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。(出自编程之美)

分析:一个数组中有很多数,现在我们要找出这个数组中那个超过出现次数一半的数字,怎么找呢?
大凡当我们碰到某一个杂乱无序的东西时,考虑是否能够通过排序来解决问题:
如果数组无序,可以先把数组中所有这些数字先进行排序,至于选取什么排序方法则不在话下,最常用的快速排序O(N*logN)即可。排完序呢,直接遍历。在遍历整个数组的同时统计每个数字的出现次数,然后把那个出现次数超过一半的数字直接输出,题目便解答完成了。总的时间复杂度为O(N*logN+N)。
但再想想,排序之后,真的还需要再遍历一次整个数组么?我们发现,一个数字在数组中的出现次数超过了一半,那么在已排好序的数组索引的N/2处(从零开始编号),就一定是这个数字。自此,我们只需要对整个数组排完序之后,然后直接输出数组中的第N/2处的数字即可,这个数字即是整个数组中出现次数超过一半的数字,总的时间复杂度由于少了最后一次整个数组的遍历,缩小到O(N*logN)。

但是不论是上述思路一的O(N*logN+N),还是思路二的O(N*logN),时间复杂度并无本质性的改变。我们需要找到一种更为有效的思路或方法。
我们可以试着这么考虑,如果每次删除两个不同的数(不管是不是我们要查找的那个出现次数超过一半的数字),那么,在剩下的数中,我们要查找的数(出现次数超过一半)出现的次数仍然超过总数的一半。
证明:如果x代表数组中出现次数超过一半的数(记为A)的出现总次数,y代表数组元素总个数。则2x>y。分两种情况讨论:删除的2个不同的数中,都是非A,那么删除之后,x=x,y=y-2, 此时,2x > y-2成立。如果删除的2个不同的数,一个是A,一个是非A,则x=x-1,
y=y-2, 此时,2(x-1)>y-2成立。得证。
通过不断重复这个过程,不断排除掉其它的数,最终找到那个出现次数超过一半的数字。这个方法,免去了上述排序过程,总得说来,时间复杂度只有O(N),空间复杂度为O(1),不失为最佳方法。

可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字res,一个是次数time。
初始情况res=a[0], time=1,如果下一个数字a[1]和我们之前保存的数字res不同,则次数减1。time=0,则表示将a[0]和a[1]都删除了,置res为下一个数,即a[2], time=1。
如果下一个数字a[1]和我们之前保存的数字res相同,则次数加1。time=2,则表示将a[0]和a[1]都不能删除,继续扫描数组。

如果扫描当前数时,time=0,表示之前的数都删除了,置当前数为第一个数,time=1.
如果当前数与res相同,则time++,表明res目前出现了time次。
如果当前数与res不同,则time--, 如果time不为0,说明删除了当前数与之前保存的res中的一个。如果time为0,表示之前的数都删除了,置当前数为第一个数,time=1.
代码如下:
void morethanhalf(int *set, int len)
{
int res, time;
int i, j;

for(i = time = 0; i < len;i++)
{
if(time== 0)
{
res= set[i];
time= 1;
}
else
{
if(set[i]== res)time++;
elsetime--;
}
}
//判断输入数组中,是否包含出现次数大于一半的数
for(i = 0; i < len; i++)
{
if(set[i] == res)
{
time++;
}
}

if(time> len/2)
{
printf("thenumber is %d\n", res);
}
else
{
printf("thereis no number times more than half\n");
}
}

扩展:数组中有一个数字出现的次数正好是数组长度的一半,找出这个数字。
分析:继续采用上面的思想,每次删除2个不同的数,那么最后剩下的2个数中,一定有一个是要找的数。
只要在上述代码的基础上,继续判断res出现的次数是否为数组长度的一半。如果是,则res就是要找的数,如果不是,那么a[n-1]就是要找的数。
代码如下:
int halfofset(int *set, int len)
{
int res, time;
int i, j;

for(i = time = 0; i <len; i++)
{
if(time == 0)
{
res =set[i];
time = 1;
}
else
{
if(set[i]== res)time++;
elsetime--;
}
}

time = 0;
for(i = 0; i < len; i++)
{
if(set[i] ==res)time++;
}

if(time ==len/2)return res;
return set[len-1];
}


(http://blog.csdn.net/v_july_v/article/details/6890054)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: