您的位置:首页 > 职场人生

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

2014-09-08 20:04 561 查看
/*
数组中出现次数超过一半的数字:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如,输入一个长度为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。
样例输入:
9
1 2 3 2 2 2 5 4 2
8
1 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];//轴:axis
while(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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐