题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
分析:思路一,如果是排序的数组那就好了。快排一次可以确定一个位置的数字,在他的左侧都比他小,右侧都比他大。一个数组中数字超过数组长度一半,经过排序之后,中间的位置一定是这个数字,这个数字也就是统计学上的中位数。
解法一:当且仅当可以修改数组内容时,利用快排分区,排好数组中间位置的数字即为结果,记得要检验是否无效输入;
#include <stdlib.h>
#include <stdio.h>
//快排一次分区
int partition(int *a, int low, int high)
{
int value = a[low]; //基准元素
while (low < high)
{
while (low < high && value <= a[high])
--high;
if (low < high) a[low++] = a[high];
while (low < high && a[low] <= value)
++low;
if (low < high) a[high--] = a[low];
}
a[low] = value;
return low;
}
bool inputInvalid = false;
//检查是否真的超过一半
bool CheckInvalidArray(int *numbers, int length, int num)
{
inputInvalid = false;
int times = 0;
for (int i = 0; i < length; ++i)
{
if (numbers[i] == num)
++times;
}
bool res = true;
if (times * 2 <= length)
{
inputInvalid = true;
res = false;
}
return res;
}
//解法一:快排求第k大的数值,需要改变数组
int MoreThanHalfNum(int *number, int length)
{
if (number == NULL || length <= 0)
{
inputInvalid = true;
return 0;
}
int middle = length >> 1;
int start = 0;
int index = partition(number, 0, length-1);
while (index != middle)
{
if (index < middle)
{
index = partition(number, index+1, length-1);
}
else {
index = partition(number, 0, index - 1);
}
}
int result = number[middle];
//这里补充一个检查是不是超过一半的判断
if (!CheckInvalidArray(number, length, result))
result = 0;
return result;
}
解法二:不改变数组本身,利用数组特性。两个变量,一个result保存数字,一个count保存次数。result初始化为第一个元素,count初始化为1;从第二个元素开始遍历数组,如果count==0则result=当前数字,count = 1;否则如果result=当前数字,count++;否则--count;这样result必定是超过一半的元素,当然最后还是要检验是否有效输入;
//解法二:根据数组特点,不改变数组本身
int MoreThanHalfNum2(int *numbers, int length)
{
if (numbers == NULL || length <= 0)
{
inputInvalid = true;
return 0;
}
int result = numbers[0];
int count = 1;
for (int i = 1; i < length; ++i)
{
if (count == 0)
{
result = numbers[i];
count = 1;
}
else if (numbers[i] == result)
++count;
else
--count;
}
if (!CheckInvalidArray(numbers, length, result))
result = 0;
return result;
}
int main()
{
int a[] = {1,2,3,2,2,2,5,4,2};
int res = MoreThanHalfNum(a, 9);
printf("%d\n", res);
int b[] = { 1,2,3,2,2,2,5,4,2 };
int res2 = MoreThanHalfNum2(b, 9);
printf("%d\n", res2);
getchar();
return 0;
}
测试结果:
总结:快排一次分区可以在O(n)时间找到数组中任意第K大的数字,利用好这一点可以解决很多问题,但是会修改数组本身内容,这一点面试的时候一定要问清楚。两种解法时间复杂度都是O(n)。