剑指Offer29数组中出现次数超过一半的数字

题目:

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2.


分析:

看到这个题的时候我首先想的是加一个辅助数组,统计出每一个数出现的次数,因为题目中给的数组是不知道的所以不能用哈希存储的方法,那么只能用普通方法,则对其查找的时间复杂度就是O(n2)。所以这个方式肯定是不合适的。

那么我们就又再想如果这个数组是排序的话,那么是不是会方便一些,肯定会方便一些,排序的时间复杂度是O(nlgn)。

如果说还有更快的,你能想到吗?


方法1:

仔细分析题目咱们会发现题目中所说的超过一半这个问题,咱们一直把它当做问题,用来最后判断是不是符合条件,那么咱们能不能把它翻过来用,让这个问题转化成我们的条件?   对!如果把数组排序的话,那么超过一半的数,肯定位于中间,所以也就是说那个超过一半的数字,肯定是这个数组的中位数!我们再努力一点,想啊!如果是中位数的话,那么是不是就是它的左边都比它小或者等于,右边都比它大或者等于!

这时如果我们能把只是迁移一下就会意识到快速排序中的Partition()函数的作用,它随机选取一个数,让比它大的都在右边,比它小的都在左边。这不正是我们需要的吗!?

也就是说如果这个随机数正好位于中间的话,那么这个数就是我们需要的啦!!


方法2;

接下来我们从另外一个角度分析来解决这个问题,我们还是抓住数组中有一个数字出现的次数超过数组长度的一半,这句话来当突破口。也就是说有一个数字出现的次数比其他所有的数字出现的总和还要多。因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1,如果不同则次数减1,如果次数为0,就保存下一个数字,并把次数设置为1,由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。


方法1的代码:

#include <iostream>
#include <time.h>


#include <stdlib.h>
using namespace std;


bool g_bInputInvalid = false;


int Random(int start,int end)
{
     srand(time(NULL));
    return start + rand()%(end-start+1);
}
void Swap(int *a,int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
int Partation(int *data,int length ,int start,int end)
{
    if(data == NULL || length < 0 || start <0 || end >=length)
    {
        cout<<"出错了"<<endl;
        return -1;
    }
    int index = Random(start,end);
    Swap(&data[end],&data[index]);
    int small = start - 1;
    for(index = start ;index < end;index ++)
    {
        if(data[index] < data[end])
        {
            small ++;
            if(small != index )
                Swap(&data[index],&data[small]);
        }
    }
    small ++;
    Swap(&data[small],&data[end]);
    return small;
}
int CheckMoreThanHalf(int *data,int length,int num)
{
    int time = 0;
    for(int i=0;i<length;i++)
    {
        if(data[i] == num)
            time++;
    }
    if(time*2<length)
    {
        cout<<"这个不是"<<endl;
        return false;
    }


    return true;
}
int MoreThanHalfNum(int *data,int length)
{
    if(data== NULL || length <0)
    {
        return 0;
    }
    int middle = length>>2;
    int start =0,end = length-1;
    int index = Partation(data,length,start,end);
    while(index != middle)
    {
        if(middle < index)
        {
            end = index - 1;
            index = Partation(data,length,start,end);
        }
        else
        {
            start = index + 1;
            index = Partation(data,length,start,end);
        }
    }
    int result = data[middle];
    if(!CheckMoreThanHalf(data,length,result))
        result = 0;
    cout<<"找到了!"<<endl;
    return result;
}
void test()
{
    int data[]={1,2,3,2,2,2,5,4,2};
    int length = 9;
    int result = MoreThanHalfNum(data,length);
    cout<<result <<endl;
}
int main()
{
    test();
    cout << "Hello world!" << endl;
    return 0;
}



方法2代码:


<pre name="code" class="cpp">int MoreThanHalfNumFangFa2(int *data,int length)
{
    if(data == NULL && length < 0)
    {
        cout<<"数组错误"<<endl;
        return -1;
    }
    int result = data[0];
    int time = 1;
    for(int i=1;i<length;i++)
    {
        if(time ==0)
        {
            result = data[i];
            time = 1;
        }
        else if(data[i] == result)
            time++;
        else if(data[i] != result)
            time --;
    }
    if(!CheckMoreThanHalf(data,length,result))
        result = 0;
    cout<<"找到了!"<<endl;
    return result;
}


 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值