题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2出现了5次,超过数组长度的一半,因此输出2.
分析:最直接方法,先对数组进行排序,然后统计每个数出现的次数就可以找出该数字。时间复杂度为O(nlogn)。我们可以进一步找出更快的方法。
方法1:由于该数字出现次数大于数组的一半,因此该数字必定是数组的中位数。我们先选择数组中的数,然后将其他数字分为两组,比选择的数大的放在右边,小的放左边,然后观察这个数的下标,如果大于n/2,则中位数在其左边,等于的话该数就是要找的数,否则就是在右边,然后在进行递归就可以。这样的算法的时间复杂度为O(n)。实现如下:
int MoreThanHalfNum(int* numbers,int length){ if(CheckInvalidArray(numbers,length)) return 0; int middle=length>>1; int start=0; int end=length-1; int index=Partition(numbers,length,start,end); while(index!=middle) { if(index>middle) { end=index-1; index=Partition(numbers,length,start,end); } else { start=index+1; index=Partition(numbers,length,start,end); } } int result=numbers[middle]; if(!CheckMoreThanHalf(numbers,length,result)) result=0; return result;}bool g_bInputInvalid=false;bool CheckInvalidArray(int* numbers,int length){ g_bInputInvalid=false; if(numbers==NULL||length<=0) g_bInputInvalid=true; return g_bInputInvalid;}int Partition(int data[],int length,int start,int end){ if(data==NULL||length<=0||start<0||end>=length) throw new std::exception("Invalid Parameters"); int index=RandomInRange(start,end); Swap(&data[index],&data[end]); int small=start-1; for(index=start;index<=length) { g_bInputInvalid=true; idMoreThanHalf=false; } return isMoreThanHalf;}
方法2:根据数组特点找出数字。我们在遍历数组的时候保存两个值,一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字时,如果下一个数字和我们之前保存的数字相同,则次数加1,如果不同,则次数减1.如果次数为0,我们保存下一个数字并设次数为1.如果我们要找的数字出现次数比所有其他数字出现的次数之和还要多,则那个数字肯定最后一次把次数设为1的那个数。实现如下:其中CheckInvalidArray函数和CheckMoreThanHalf函数和上面方法中的相同,这里不在重复。
int MoreThanHalfNum(int* numbers,int length){ if(CheckInvalidArray(numbers,length)) return 0; int result=numbers[0]; int times=1; for(int i=1;i
这两种方法中,方法1需要修改数组,如果不允许修改数组,则只能用方法2来解答。