题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路一:
分析:数组中若一个数字出现次数超过一半,则一定比其他所有元素出现次数的和多,若从数组头开始遍历,令num=array[0],设一个count变量,初值1,遇到相同元素count加一,不同元素count减1,,当count==0时候,更新num令它等于新遇到的元素值。最终,我们需要求的出现次数超过一半的元素一定是最后一次让count等于1对应的元素值。整个算法的时间复杂度是O(n)
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
int n = numbers.size();
if (n == 0) return 0;
int num = numbers[0], count = 1;
for (int i = 1; i < n; i++) {
if (numbers[i] == num) count++;
else count--;
if (count == 0) {
num = numbers[i];
count = 1;
}
}
// 输入数组可能不存在个数超过一半的元素,若不存在,返回0
count = 0;
for (int i = 0; i < n; i++) {
if (numbers[i] == num) count++;
}
if (count * 2 > n) return num;
return 0;
}
};
思路二:
基于Partition函数的时间复杂度是O(n)的算法
分析:易知,若数组中有出现次数超过一半的元苏,若将数组排序,数组中间的数字一定是出现次数超一半的数字,排序的时间复杂度是O(nlogn)
基于Partition函数的时间复杂度是O(n)的算法
先从数组中随机选一个元素,然后调整数组中数字顺序,使比选中后数字小的放选中数的左边,比选中数大的放它右边,如果选中数字经过一次Partition后的下标刚好是n/2,则待求的数字就是Partition后的array[n/2];若随机选的数字在Partition后下标大于n/2,则中位数(待求数字)应该在Partition后随机数的左边 ,否则在右边。递归求解
#include <algorithm>
#define random(a,b) (rand()%(b-a+1)+a)
class Solution {
public:
int Partition(vector<int> data, int length, int start, int end)
{
int pivot = random(start, end);
swap(data[pivot], data[end]);
int small = start - 1;
for (int index = start; index<end; index++)
{
//如果index所指向的元素小于pivot枢纽元素的话,index和small游标一起向右走,当index所指向的元素大于或者等于pivot枢纽元素的话,只是index自己向右走,small不动,这样的话index走啊走啊 发现有一个小于pivot枢纽元素的时候,将此元素与small所指向的元素互换
if (data[index]<data[end])//此处若改成大于号则从大到小排
{
++small;
if (small != index)
{
swap(data[index], data[small]);
}
}
}
++small;
//最后把small所指向元素与枢纽元素pivot互换
swap(data[small], data[end]);
return small;
}
int MoreThanHalfNum_Solution(vector<int> numbers) {
int length=numbers.size();
if(length==0)
return 0;
int middle=length>>1;
int start=0;
int end=length-1;
int randomIndex=Partition(numbers,length,start,end);
while(randomIndex!=middle)
{
if(randomIndex>middle)
{
end=randomIndex-1;
randomIndex=Partition(numbers,length,start,end);
}
else
{
start=randomIndex+1;
randomIndex=Partition(numbers,length,start,end);
}
}
int result=numbers[middle];
int times=1;//验证下上面的到的result是不是在数组中出现次数超过一半
for(int i=1;i<length;++i)
{
if(numbers[i]==result)
times++;
}
if(times*2<=length)
return 0;
else
return result;
}
};
关于快速排序和Partition函数,看我的这篇博文
https://blog.csdn.net/qq_34793133/article/details/80610016
关于C++产生随机数