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

来源:互联网 发布:js给textbox赋值 编辑:程序博客网 时间:2024/04/29 23:53
/*数组中出现次数超过一半的数字:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如,输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。1 基于Partition函数的O(n)解法:如果将数组排序,那么数组中间的数字时出现次数超过数组长度一半的数字。用随机选择快速排序算法,在数组中随机选择一个数字,如果选中的数字的下标刚好是n/2,这个数字就是中位数。如果选中的下标大于n/2,那么中位数在它的左边。注意:最后还需要对返回的数字,检查其是否次数超过一半2根据数组特点找出O(n)的算法:数组中有一个数字出现的次数超过数组长度的一半 = 出现次数>其他所有数字出现次数和遍历数组时保存两个值:1数组中的数字,2次数当遍历到下一个数字的时候:如果下一个数字和之前保存的数字相同,则次数累加                                                        不同,次数减1当次数为0时:我们需要保存下一个数字,并将次数设为1。要找的数字出现的次数比其他所有数字出现的次数之和都要多,因此要找的数字肯定是最后一次把次数设为1时对应的数字。?为什么是最后一次设为1输入:每个测试案例包括2行:第一行输入一个整数n(1<=n<=100000),表示数组中元素的个数。第二行输入n个整数,表示数组中的每个元素,这n个整数的范围是[1,1000000000]。输出:对应每个测试案例,输出出现的次数超过数组长度的一半的数,如果没有输出-1。样例输入:91 2 3 2 2 2 5 4 281 2 3 2 2 5 4 2样例输出:2-1*//*关键:1 基于Partition函数的O(n)解法:如果将数组排序,那么数组中间的数字时出现次数超过数组长度一半的数字。用随机选择快速排序算法,在数组中随机选择一个数字,如果选中的数字的下标刚好是n/2,这个数字就是中位数。如果选中的下标大于n/2,那么中位数在它的左边。2 swap(&iArr[low],&iArr[iIndex]);//注意:快速排序之随机枢轴版中,注意要将随机枢轴与iArr[low]进行交换3 int iIndex = partition(low,high);while(iIndex != iMid){if(iIndex > iMid)//如果下标大于中间,应该向左缩小范围{high = iIndex - 1;}4 for(int i = 0 ; i < iLen ; i++)//注意,需要检查是否真正出现的次数超过了一半{if(iRes == iArr[i]){iCnt++;}}if(iCnt*2 <= iLen)5 根据数组特点找出O(n)的算法:数组中有一个数字出现的次数超过数组长度的一半 = 出现次数>其他所有数字出现次数和要找的数字出现的次数比其他所有数字出现的次数之和都要多,因此要找的数字肯定是最后一次把次数设为1时对应的数字。?为什么是最后一次设为1。因为只有最后一次设为1说明相同的数字个数>不同的数字个数6 if(iTimes == 0)//如果次数减为0,就令结果为当前数,并使次数为1{iRes = iArr[i];iTimes = 1;}else if(iRes == iArr[i])//如果当前数与之前保存的数相同,就累加{iTimes++;}else//如果当前数与之前保存的数不同,就累减*/#include <stdio.h>#include <string.h>#include <stdlib.h>const int MAXSIZE = 100001;int iArr[MAXSIZE];//注意:随机选择枢轴的排序版本与原版本不能混淆,会出事int randomInRange(int min,int max){return (rand() % (max - min + 1) + min);//注意这里采用:取余来限定范围,通过加上min来获取[min,max]指定范围内的数}void swap(int* p1,int* p2){int iTemp = *p1;*p1 = *p2;*p2 = iTemp;}int partition(int low,int high){int iIndex = randomInRange(low,high);swap(&iArr[low],&iArr[iIndex]);//注意:快速排序之随机枢轴版中,注意要将随机枢轴与iArr[low]进行交换int iAxis = iArr[low];//轴:axiswhile(low < high){while(low < high && iArr[high] >= iAxis){high--;}iArr[low] = iArr[high];while(low < high && iArr[low] <= iAxis){low++;}iArr[high] = iArr[low];}iArr[low] = iAxis;return low;}int moreThanHalfNum(int iLen){int iMid = iLen >> 1;int low = 0,high = iLen -1;int iIndex = partition(low,high);while(iIndex != iMid){if(iIndex > iMid)//如果下标大于中间,应该向左缩小范围{high = iIndex - 1;}else{low = iIndex + 1;}iIndex = partition(low,high);}int iRes = iArr[iMid];int iCnt = 0;for(int i = 0 ; i < iLen ; i++)//注意,需要检查是否真正出现的次数超过了一半{if(iRes == iArr[i]){iCnt++;}}if(iCnt*2 <= iLen){return -1;}else{return iRes;}}int moreThanHalfNum2(int iLen){int iRes = iArr[0];int iTimes = 1;for(int i = 1 ; i < iLen ;i++){if(iTimes == 0)//如果次数减为0,就令结果为当前数,并使次数为1{iRes = iArr[i];iTimes = 1;}else if(iRes == iArr[i])//如果当前数与之前保存的数相同,就累加{iTimes++;}else//如果当前数与之前保存的数不同,就累减{iTimes--;}}int iCnt = 0;for(int i = 0 ; i < iLen ; i++){if(iRes == iArr[i]){iCnt++;}}if(iCnt*2 <= iLen){return -1;}else{return iRes;}}void process(){int n;while(EOF != scanf("%d",&n)){memset(iArr,0,sizeof(iArr));for(int i = 0 ; i < n ; i++){scanf("%d",&iArr[i]);}printf("%d\n",moreThanHalfNum(n));printf("%d\n",moreThanHalfNum2(n));}}int main(int argc,char* argv[]){process();getchar();return 0;}


 

0 0
原创粉丝点击