算法题005 剑指Offer面试题29 数组中出现次数超过一半的数字

时间:2021-01-29 11:06:23

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

题目来源

  九度Online Judge: http://ac.jobdu.com/problem.php?cid=1039&pid=12

 

题目描述:

  数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

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

 

输入:

  每个测试案例包括2行:

  第一行输入一个整数n(1<=n<=100000),表示数组中元素的个数。

  第二行输入n个整数,表示数组中的每个元素,这n个整数的范围是[1,1000000000]。

 

输出:

  对应每个测试案例,输出出现的次数超过数组长度的一半的数,如果没有输出-1。

 

样例输入:

9

1 2 3 2 2 2 5 4 2

9

1 1 1 2 2 2 3 3 3

样例输出:

2

-1

 

题目分析

解法一:基于快排中分割算法的方法

  数组中有一个数字出现的次数超过了数组长度的一半。如果把这个数组排序,那么排序之后位于数组中间的数字一定就是那个出现次数超过数组长度一半的数字。

  也就是说,这个数字是统计学上的中位数,即长度为n的数组中第n/2大的数字。我们有成熟的O(n)算法得到数组中任意第k大的数字。

  这种算法是受快速排序算法的启发,在随机快排中,先随机选择一个数字,然后将比它小的数字都放在它的左边,比它大的都放在它的右边。

  如果调整好位置后,这个选中的数字的下标刚好是n/2,那么这个数字就是数组中的中位数。

  如果它的下标大于n/2,那么中位数应该在它的左边,可以接着在它的左边查找;下标小于n/2的情况类似,这是一个典型的递归过程。

  当然,在这其中要考虑输入无效的情况(数组指针为NULL);也要考虑如果输入的数组中出现频率最高的数字并没有达到出现次数超过数组长度一半的情况。

 

解法二:根据数组特点找出O(n)的算法

  数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现的次数的和还要多

  因此我们可以考虑用两个变量:一个保存一个数字,一个保存次数。

  开始时,保存数组中第一个元素,次数设置为1;

  遍历数组:

  如果下一个数字和之前保存的数字相同,则次数递增1;

  如果下一个数字和之前保存的数字不同,则次数递减1;

  如果次数为零,我们需要保存下一个数字,并把次数设为1。

  由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。

  但是最后还是需要检查一下该数字的出现次数是否超过了数组长度的一半,因为可能数组中并不包含这样的数字。

 

题目解答

   代码如下:

#include <iostream>
using namespace std;


bool IsMoreThanHalf(long num[], long len, long number);

int main(int argc, char* argv[])
{
    long n = 0;
    long num[100000];

    long result;
    long times;

    while(cin >> n)
    {
        //输入数组
        for(int i = 0; i < n ; ++i)
        {
            cin >> num[i];
        }

        //开始统计
        result= num[0];
        times = 1;

        for(int i = 1; i< n; ++i)
        {
            if(0 == times)
            {
                result = num[i];
                times = 1;

            }
            else if(result == num[i])
            {
                ++times;
            }
            else
            {
                --times;
            }
        }    

        if(times > 0 && IsMoreThanHalf(num,n,result))//检查是否超过半数
        {
            cout << result << endl;
        }
        else
        {
            cout << -1 <<endl;
        }

    }

    return 0;
}


//验证在长度为len的数组num中,number是否出现次数过半
bool IsMoreThanHalf(long num[], long len, long number)
{
    bool isMoreThan = false;

    long count = 0;

    for(int i = 0; i<len; ++i)
    {
        if(number == num[i])
        {
            ++count;
        }
        if(count > len/2)
        {
            isMoreThan = true;
            break;
        }
    }


    return isMoreThan;
}

 

  本代码能够完成功能,并且在九度的Online Judge提交AC。

 

参考资料

  《剑指Offer》面试题29。

  相关博客链接:http://www.cnblogs.com/remlostime/archive/2012/11/22/2782198.html