题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解法一
简单粗暴,时间复杂度O(nlogn)。
- 先快排 ,可参考 快速排序。
- 再统计。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.size()==0)
return 0;
int left = 0, right = numbers.size()-1;
QuickSort(numbers, left, right);
int count = 0;
for(int i=0; i<numbers.size(); i++)
{
if(numbers[i] == numbers[numbers.size()/2])
count ++;
}
if(count > numbers.size()/2)
return numbers[numbers.size()/2];
else
return 0;
}
void QuickSort(vector<int>& numbers, int left, int right)
{
if(left < right)
{
int pivot = OnceSort(numbers, left, right);
QuickSort(numbers, left, pivot-1);
QuickSort(numbers, pivot+1, right);
}
}
int OnceSort(vector<int>& numbers, int left, int right)
{
int i = left, j = right;
while(i < j)
{
while(i < j && numbers[j] > numbers[left])
j--;
while(i < j && numbers[i] <= numbers[left])
i++;
swap(numbers[i], numbers[j]);
}
swap(numbers[i], numbers[left]);
return i;
}
};
解法一的简化版:
使用C++ 的 STL
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.size()==0)
return 0;
sort(numbers.begin(), numbers.end());
int count = 0;
for(int i=0; i<numbers.size(); i++)
{
if(numbers[i] == numbers[numbers.size()/2])
count ++;
}
if(count > numbers.size()/2)
return numbers[numbers.size()/2];
else
return 0;
}
};
解法二:基于partition函数的时间复杂度为O(n)的算法
partition 函数是完成快速排序的基础,可参考 排序-快速排序
算法思想
- 在随机快速排序算法中,我们先在数组中随机选择一个数字(这里我采用的是以第一个数字作为 partition 的基准数),然后调整数组中数字的顺序。
- 如果这个数字比基准数小,则排在它的左边,如果大则排在基准数的右边,
- 如果这个选中的基准数下标刚好是 n/2。那么就对这个数字进行统计,如果它的个数超过数组长度的一半,则返回这个基准数,否则,返回0。
- 如果下标 小于 n/2,那么继续在基准数的右边继续划分。
- 如果小标大于n/2, 那么继续在基准数的左边继续划分。
- 直到基准数的位置为n/2时,进行统计并判断。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.size()==0)
return 0;
int left = 0, right = numbers.size() - 1;
int pivot = partition(numbers, left, right);
int mid = numbers.size() / 2;
while(mid != pivot)
{
if(pivot < mid)
{
left = pivot + 1;
pivot = partition(numbers, pivot + 1, right);
}
else
{
right = pivot - 1;
pivot = partition(numbers, left, pivot - 1);
}
}
// 统计数量
int count = 0;
for(int i=0; i<numbers.size(); i++)
{
if(numbers[i] == numbers[mid])
count ++;
}
if(count > numbers.size() / 2)
return numbers[mid];
else
return 0;
}
int partition(vector<int> & numbers, int left, int right)
{
int i = left, j = right;
while(i < j)
{
while(i < j && numbers[j] > numbers[left])
j--;
while(i < j && numbers[i] <= numbers[left])
i++;
swap(numbers[i], numbers[j]);
}
swap(numbers[i], numbers[left]);
return i;
}
};
解法三:根据数组特点找出时间复杂度为O(n)的算法
考虑数组中有一个数字出现的次数超过数组长度的一半,也就是说这个数字出现的次数比其他所有的数字出现的次数的和都要多。算法中保存两个值:1、一个是数组中的某个数字。2、另一个是次数。
遍历到下一个数字的时候,如果次数为0,那么保存遍历的下一个数字,同时次数加1。
如果遍历的下一个数字和我们保存的数字相等,那么次数加1。
如果遍历的下一个数字和我们保存的数字不相等,那么次数减1。
最后遍历整个数组,统计保存的数字出现的次数是否超过数组长度的一半。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.size()==0)
return 0;
int count = 1;
int result = numbers[0];
for(int i=1; i<numbers.size(); i++)
{
if(count==0)
{
result = numbers[i];
count = 1;
}
else if(result == numbers[i])
count++;
else
count --;
}
int a = 0;
for(int i=0; i<numbers.size(); i++)
{
if(numbers[i] == result)
a++;
}
if(a*2 > numbers.size())
return result;
else
return 0;
}
};