zoukankan      html  css  js  c++  java
  • 18数组中出现次数超过一半的数

        题目描述:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。(出自编程之美)

     

        分析:一个数组中有很多数,现在我们要找出这个数组中那个超过出现次数一半的数字,怎么找呢?

           大凡当我们碰到某一个杂乱无序的东西时,考虑是否能够通过排序来解决问题:

        如果数组无序,可以先把数组中所有这些数字先进行排序,至于选取什么排序方法则不在话下,最常用的快速排序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 ", res);

             }

             else

             {

                      printf("thereis no number times more than half ");

             }

    }

     

        扩展:数组中有一个数字出现的次数正好是数组长度的一半,找出这个数字。

        分析:继续采用上面的思想,每次删除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)

  • 相关阅读:
    MATLAB 制作gif动画
    MATLAB求解浸润角
    MATLAB 解代数方程组
    空间直线同任意形状椭球交点
    空间旋转——四元数表示
    坐标系旋转 & 坐标点旋转
    展开循环来加速计算
    空间直线同球体交点求解
    变量初始化问题
    python 实现多个线程间消息队列传递,一个简单的列子
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247169.html
Copyright © 2011-2022 走看看