1、题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
解法一:
如果把这个数组排序,那么排序之后位于数组中间的数字一定就是那个出现次数超过数组长度一半的数字,即中位数,是长度为n的数组中第n/2大的数字。我们有成熟的O(n)的算法得到数组中任意第k大的数字。
此算法基于快速排序的启发。
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;
}
上述代码中函数Partition时完成快排的基础,此处不再实现。
2、如果函数的输入参数是一个指针(数组在参数传递的时候退化为指针),就要考虑这个指针可能为NULL。下面的函数CheckInvalidArray用来判断输入的数组是不是无效的。
题目中说数组中有一个数字出现次数超过数组长度的一半,如果输入的数组中出现频率最高的数字都没有达到这个标准该怎么办?这就是定义函数CheckMoreThanHalf的原因。
下面的代码用一个全局变量表示输入无效的情况。
bool g_bInputInvalid = false;
bool CheckInvalidArray(int* numbers, int length)
{
g_bInputInvalid = false;
if(numbers == NULL || length <= 0)
g_bInputInvalid = true;
return g_bInputInvalid;
}
bool CheckMoreThanHalf(int* numbers, int length, int number)
{
int times = 0;
for(int i=0; i<length; ++i)
{
if(numbers[i] == number)
times++;
}
bool isMoreThanHalf = true;
if(time * 2 <= length)
{
g_bInputInvalid = true;
isMoreThanHalf = false;
}
return isMoreThanHalf;
}
解法二:
数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现次数的和还要多。因此我们可以考虑在变量数组的时候保存两个值:一个是数组中的一个数字,一个是次数。
当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。若不同则次数减1。如果次数为零,我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。
int MoreThanHalfNum(int* numbers, int length)
{
if(CheckInvalidArray(numbers, length))
return 0;
int result = numbers[0];
int times = 1;
for(int i=1; i<length; ++i)
{
if(times == 0)
{
result = numbers[i];
times = 1;
}
else if(numbers[i] == result)
times++;
else
time--;
}
if(! CheckMoreThanHalf(numbers, length, result))
result = 0;
return result;
}
和解法一一样,也要检验输入的数组是不是有效的,同上不再重复。