找出在数组中出现次数超过一半的那个数

来源:互联网 发布:windows触摸屏无法使用 编辑:程序博客网 时间:2024/05/22 17:03

也就是编程之美那道“寻找发帖水王的题目”, 开始看这道题目的时候,首先想到就是遍历,遍历数组所有的元素,比较元素出现的次数是否大于1/2.思路很简单,直接附上源代码。

int FindMoreHalfNumber1(int* array,int n){assert(array!=NULL||n<1);for(int i=0;i<n;++i){int nTimes=0;for(int j=0;j<n;++j){if(array[j]==array[i])++nTimes;}if(nTimes>n/2)return array[i];}}

暴力破解的结果就是时间复杂度为O(n2),出现在编程之美上的题目,肯定不会用暴力破解的方式,一定有更快捷的方法。

既然是求每个元素出现的次数,那么使用哈希表。因为int型的范围过大,我们不能使用数组来定义哈希表。C++标准库有一个map容器。

int FindMoreHalfNumber2(int* array,int n){assert(array!=NULL||n<1);map<int,int>num_count;for(int i=0;i<n;++i)++num_count[array[i]];map<int,int>::iterator it=num_count.begin();int target=it->first;int nTimes=it->second;for(++it;it!=num_count.end();++it){if(it->second>nTimes){nTimes=it->second;target=it->first;}}return target;}

代码本身来看,只遍历一次数组,遍历一次map容器,时间复杂度好像很低。但是实际不然,熟悉map容器的人都知道,map容器内部是按照键值进行排序,数组元素插入容器中的开销也是很大,尤其是当数组元素种类很多的时候。这样的效率也不是很理想。

书中作者给出的暴力解法是排序,因为要找的数字出现次数超过一半,排好序以后n/2处的肯定是要找的数。排序我们使用快速排序

void QuickSort(int* array,int begin,int end){if(begin<end){int i=begin,j=end;int key=array[i];while(i<j){while(i<j&&array[j]>=key)--j;if(i<j)array[i++]=array[j];while(i<j&&array[i]<key)++i;if(i<j)array[j--]=array[i];}array[i]=key;QuickSort(array,begin,i-1);QuickSort(array,i+1,end);}}int FindMoreHalfNumber3(int* array,int n){assert(array!=NULL||n<1);QuickSort(array,0,n-1);return array[n/2];}

快排的时间复杂度为O(nlogn),找出这个数的时间复杂度也就是O(nlogn)

书中作者最后给出的代码是我无论如何想不出来的,思路是这样的,既然出现次数超过一半,那么如果每次删除两个不同的数字(不管是否包含我们要找的那个数),在那个超过一半的数在剩下元素中一定还超过一半,重复上述过程,降低数组总数。代码如下:

int FindMoreHalfNumber4(int* array,int n){       assert(array!=NULL||n<1);       inttarget=array[0];       intnTimes=1;       for(inti=1;i<n;++i)       {              if(nTimes==0){                     target=array[i];                     nTimes=1;              }              else if(target==array[i])                     ++nTimes;              else                     --nTimes;       }       return target;}

时间复杂度为O(n),空间复杂度为O(1)。这个思路太好了,非常新奇。这个思路很强的约束条件,必须超过一半,等于一半都不可以。好像是不是腾讯吧,把这道稍微变动了一下,找出出现次数大于等于一半的那个数,有些人可能会一时被问住,对呀,如果等于一半呢?之前似乎没考虑过哎,其实很简单啦。等于一半只可能出现在数组元素个数为偶数的情况下,我们只需要选择第一个元素或者最后一个元素,先判断它是不是我们想要找的,如果不是,不用考虑它,这样不就转换成超过一半的这种情况了吗?

int FindMoreHalfNumber5(int* array,int n){assert(array!=NULL||n<1);if(n%2==0){int count=0;for(int i=0;i<n-1;++i){if(array[i]==array[n-1])++count;}if(count>=n/2)return array[n-1];//当最后一个元素不是我们要找的那个元素的时候,不考虑最后一个n=n-1;}int target=array[0];int nTimes=1;for(int i=1;i<n;++i){if(nTimes==0){target=array[i];nTimes=1;}else if(target==array[i])++nTimes;else--nTimes;}return target;}
最后给出编程之美这道题之后的扩展问题,找出3个出现次数超过1/4的数字。思路是一样,如果出现一个和这3个数不相等,那么删除这四个数,不影响最终的结果,给出源代码。

void FindMoreForthNumbers(int* array,intn,int& target1,int& target2,int& target3){       assert(array!=NULL||n<4);       intnTimes1=0;       intnTimes2=0;       intnTimes3=0;       for(inti=0;i<n;++i)       {              if(nTimes1==0)              {                     target1=array[i];                     nTimes1=1;              }              elseif(target1==array[i])                     ++nTimes1;              elseif(nTimes2==0)              {                     target2=array[i];                     nTimes2=1;              }              elseif(target2==array[i])                     ++nTimes2;              elseif(nTimes3==0)              {                     target3=array[i];                     nTimes3=1;              }              elseif(target3==array[i])                     ++nTimes3;              else              {                     --nTimes1;                     --nTimes2;                     --nTimes3;              }       }}







0 0
原创粉丝点击