面试题29数组中出现次数超过一半的数字

来源:互联网 发布:excel相关系数矩阵 编辑:程序博客网 时间:2024/06/07 14:46

题目描述:

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

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

题目分析

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

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

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

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

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

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

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

#include <iostream>using namespace std;bool CheckMoreThanHalf(int a[],int len,int key){int times=0;for(int i=0;i<len;i++){if(a[i]==key)times++;}bool flag=true;if(times*2<=len)flag=false;return flag;}/*int par(int a[],int len,int low,int high){      int t=a[low];      while(low<high)      {          while(low<high&&a[high]>=t)              high--;          a[low]=a[high];          while(low<high&&a[low]<=t)              low++;          a[high]=a[low];      }      a[low]=t;      return low;  }*/int par(int a[],int len,int low,int high){      int t=a[low]; int i=low,j=high;    while(i!=j)      {          while(i<j&&a[j]>=t)  j--;while(i<j&&a[i]<=t)  i++;        if(i<j){int temp=a[i];a[i]=a[j];a[j]=temp;}}a[low]=a[i];a[i]=t;    return i;  }int foo(int a[],int len){if(len<=0)return -1;int mid = len>>1;int start=0;int end=len-1;int index=par(a,len,start,end);while(index!=mid){if(index>mid){end=index-1;index=par(a,len,start,end);}else{start=index+1;index=par(a,len,start,end);}}int result=a[mid];if(!CheckMoreThanHalf(a,len,result))result=0;return result;}int main(){int a[]={1,2,3,2,2,2,5,4,2};int b[]={1,2,3,4};int lenA = sizeof(a)/sizeof(a[0]);int lenB = sizeof(b)/sizeof(b[0]);cout<<foo(a,lenA)<<endl;cout<<foo(b,lenB)<<endl;return 0;}


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

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

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

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

  遍历数组:

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

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

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

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

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

#include <iostream>using namespace std;bool CheckMoreThanHalf(int a[],int len,int key){int times=0;for(int i=0;i<len;i++){if(a[i]==key)times++;}bool flag=true;if(times*2<=len)flag=false;return flag;}int foo(int a[],int len){if(len<=0)return -1;int result=a[0];int times=2;for(int i=1;i<len;i++){if(times==0){result=a[i];times=1;}else if(a[i]==result)times++;elsetimes--;}if(!CheckMoreThanHalf(a,len,result))result=0;return result;}int main(){int a[]={1,2,3,2,2,2,5,4,2};int b[]={1,2,3,4};int lenA = sizeof(a)/sizeof(a[0]);int lenB = sizeof(b)/sizeof(b[0]);cout<<foo(a,lenA)<<endl;cout<<foo(b,lenB)<<endl;return 0;} 


阅读全文
0 0