题目:
数组中有一个数字出现次数超过数组长度的一半,请找出这个数字。例如输入长度为9的数组{1,2,3,2,2,2,5,2,3}。由于数字2在数组中出现了5次,超过数组的一半,因此输出2。
思路1:
如果我们考虑对数字进行排序,那么排序之后中间的数字一定是出现次数超过数组长度一半的数字。也就是,我们所说的“中位数 ”。考虑到我们并非一定要把整个数组排序完成,受到快速排序的partation函数的启发,那我们就只需要排序到该数组的一半的时候就能找到我们需要的数字了。
code1:
int MoreThanHalfNum(int *numbers, int length)
{
if (IsInvalidInput(numbers, length))
return 0;
int mid = length >> 1;
int begin = 0;
int end = length - 1;
int index = partation(numbers, length, begin, end);
while (index != mid)
{
if (index > mid)
{
end = index - 1;
index = partation(numbers, length, begin, end);
}
else
{
begin = index + 1;
index = partation(numbers, length, begin, end);
}
}
int result = numbers[mid];
if (!CheckMoreThanHalf(numbers, length, result))
result = 0;
return result;
}
其中,IsInvalidInput检验输入的合法性、
CheckMoreThanHalf检查该数字出现的次数是否超过数组长度的一半、
partation是快排中单次排序
bool IsInvalidInput(int* numbers, int len)
{
bool g_ValidInput = false;
if (numbers == NULL || len <= 0)
g_ValidInput = true;
return g_ValidInput;
}
bool CheckMoreThanHalf(int *numbers, int len, int result)
{
int times = 0;
for (int i = 0; i < len; ++i)
{
if (numbers[i] == result)
times++;
}
bool IsMoreThanHalf = true;
if (times * 2 < len)
IsMoreThanHalf = false;
return IsMoreThanHalf;
}
int partation(int* numbers, int length, int begin, int end)
{
if (begin < end)
{
int left = begin;
int right = end;
int key = numbers[left];
while (left!=right)
{
while (left<right && numbers[left] <= key)
left++;
while (left < right && numbers[right] >= key)
right--;
if (left < right)
std::swap(numbers[left], numbers[right]);
}
if (left != begin)
std::swap(numbers[left], numbers[begin]);
return left;
}
return -1;
}
思路2:
我们可以从数组的特点来查找。数组中有一个数字出现的次数超过数组长度的一半,也就是说这个数字出现的次数比其他所有数字出现的次数之和还要多。因此,在遍历数组的时候,我们可以考虑保存两个值:数组中的数字+次数。
当我们遍历下一个数字的 时候如果和之前保存的数字相同,那么次数加1 ;如果不同,则减1 。 由于我们要找的数字出现的次数比其他数字出现次数之和还要多,那么我们可以肯定最后一次把次数设置为1的数字就是我们需要找的。
code2:
int MoreThanHalfNum(int* numbers, int len)
{
if (IsInvalidInput(numbers, len ))
return 0;
int result = numbers[0];
int times = 1;
for (int i = 1; i < len; ++i)
{
if (result == numbers[i])
times++;
else
times--;
if (times == 0)
{
result = numbers[i];
times = 1;
}
}
if (!CheckMoreThanHalf(numbers, len, result))
result = 0;
return result;
}
两种方法的比较:
上面的两种方法的时间复杂度都是O(n) , 只是第二种方法更加直观。同时,要是不能修改数组中数字的顺序呢,那么第一种方法就不可行了。综合来看,第二种会更好。
AllOfCode:
int MoreThanHalfNum_1(int *numbers, int length) //第一种方法
{
if (IsInvalidInput(numbers, length))
return 0;
int mid = length >> 1;
int begin = 0;
int end = length - 1;
int index = partation(numbers, length, begin, end);
while (index != mid)
{
if (index > mid)
{
end = index - 1;
index = partation(numbers, length, begin, end);
}
else
{
begin = index + 1;
index = partation(numbers, length, begin, end);
}
}
int result = numbers[mid];
if (!CheckMoreThanHalf(numbers, length, result))
result = 0;
return result;
}
bool IsInvalidInput(int* numbers, int len)
{
bool g_ValidInput = false;
if (numbers == NULL || len <= 0)
g_ValidInput = true;
return g_ValidInput;
}
bool CheckMoreThanHalf(int *numbers, int len, int result)
{
int times = 0;
for (int i = 0; i < len; ++i)
{
if (numbers[i] == result)
times++;
}
bool IsMoreThanHalf = true;
if (times * 2 < len)
IsMoreThanHalf = false;
return IsMoreThanHalf;
}
int partation(int* numbers, int length, int begin, int end)
{
if (begin < end)
{
int left = begin;
int right = end;
int key = numbers[left];
while (left!=right)
{
while (left<right && numbers[left] <= key)
left++;
while (left < right && numbers[right] >= key)
right--;
if (left < right)
std::swap(numbers[left], numbers[right]);
}
if (left != begin)
std::swap(numbers[left], numbers[begin]);
return left;
}
return -1;
}
int MoreThanHalfNum_2(int* numbers, int len) //第二种
{
if (IsInvalidInput(numbers, len ))
return 0;
int result = numbers[0];
int times = 1;
for (int i = 1; i < len; ++i)
{
if (result == numbers[i])
times++;
else
times--;
if (times == 0)
{
result = numbers[i];
times = 1;
}
}
if (!CheckMoreThanHalf(numbers, len, result))
result = 0;
return result;
}