题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如,输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过了数组长度的一半,因此输出2
思路:
注意到,数组中有一个数字出现的次数超过了数组长度的一半。如果把这个数组进行排序,那么排序之后位于数组中间的数字一定就是那个出现次数超过数组长度一半的数字。也就是说,这个数字是数组的中位数。我们现在数组中随机选择一个数字,然后调整数组中数字的顺序,使得比选中的数字小的都排在左边,比选中数字大的都排在右边。如果选中的数字的下标刚好是n/2,那么这个数字就是数组的中位数:如果它的下标大于n/2,那么中位数位于它的左边,那么就从左边部分的数组中查找小于n/2就在右边进行查找。用递归的方法可以解决这个问题。还要注意判断输入的数组是否为空,以及数组中重复最多的元素都没有超过数组一半元素的情况。
代码:
bool g_bInputInvalid = false;
int Partition(int data[], int length, int start, int end)
{
if (data == nullptr || length <= 0 || start < 0 || end >= length)
throw new exception("Invalid Parameters");
int index = RandomInRange(start, end);
Swap(&data[index], &data[end]);
int small = start - 1;
for (int index = start; index < end; ++index)
{
if (data[index] < data[end]) {
++small;
if (small != index)
Swap(&data[index], &data[small]);
}
}
++small;
Swap(&data[small], &data[end]);
return small;
}
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;
}
bool g_bInputInvalid = false;
bool CheckInvalidArray(int* numbers, int length)
{
g_bInputInvalid = false;
if (numbers == nullptr || 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 (times * 2 <= length)
{
g_bInputInvalid = true;
isMoreThanHalf = false;
}
return isMoreThanHalf;
}
还有一种思路很巧妙:因为数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现的次数和还要多,那么可以在遍历数组的时候保存两个值:一个是数组中的一个数字;另一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加一;如果下一个数字和我们保存的数字不同,则次数减一。如果次数为零,那么我们需要保存下一个数字,并把次数设为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
times--;
}
if (!CheckMoreThanHalf(numbers, length, result))
result = 0;
return result;
}