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

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为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;
}

测试结果:
《剑指Offer_面试题29_数组中出现次数超过一半的数字》


总结:快排一次分区可以在O(n)时间找到数组中任意第K大的数字,利用好这一点可以解决很多问题,但是会修改数组本身内容,这一点面试的时候一定要问清楚。两种解法时间复杂度都是O(n)。

点赞