1.问题描述
数组中有一个数字超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2.(来自《剑指offer》)
2.分析
1)数组中有一个数字出现的次数超过了数组长度的一半。如果把这个数组排序那么中间的那个数字(中位数)一定是出现次数超过一半的数字,快速排序中的partition函数可以得到数组中任意第k大的数字。因此我们基于partition函数,在数组中随机选择一个数字,如果该数字在中位数的右边,那说明我们该在左边寻找中位数,反之在右边寻找中位数。
void swap(int* one,int* two)
{
int temp = *one;
*one = *two;
*two = temp;
}
int Partition(int* array,int start,int end)
{
if (array == NULL)
{
return 0;
}
int key = array[end];
int index = start;
for (int i = start; i < end; i++)
{
if (array[i] <= key)
{
swap(array + i, array + index);
index ++;
}
}
swap(array + index, array + end);
return index;
}
int isNumberMoreThanHalf(int* array,int number,int length)
{
if (array == NULL || length < 0)
{
return 0;
}
int count = 0;
for (int i = 0; i < length; i++)
{
if (array[i] == number)
{
count ++;
}
}
if (count * 2 <= length)
{
return 0;
}
return 1;
}
int MoreThanHalfNum(int *array,int length)
{
if (array == NULL || length <= 0)
{
return 0;
}
int start = 0;
int end = length - 1;
int middle = length >> 1;
int index = Partition(array,start,end);
while (index != middle)
{
if (index > middle)
{
end = index - 1;
index = Partition(array,start,end);
}
else
{
start = index + 1;
index = Partition(array,start,end);
}
}
if (isNumberMoreThanHalf(array,array[index],length))
{
return array[index];
}
else
{
return 0;
}
}
2) 这个数字肯定比别的数字出现的次数多,我们可以遍历的时候保存俩个值,一个是数组中的一个数字,一个是次数,当我们遍历下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1,如果下一个数字和我们之前保存的数字不同则次数减1.如果为0我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有的数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1对应的数字。
int MoreThanHalfNum2(int* array,int length)
{
if (array == NULL || length <= 0)
{
return 0;
}
int result = array[0];
int count = 1;
for (int i = 1; i < length; i++)
{
if(count == 0)
{
result = array[i];
count = 1;
}
if (result == array[i])
{
count ++;
}
else
{
count --;
}
}
if (isNumberMoreThanHalf(array,result,length))
{
return result;
}
else
{
return 0;
}
}