题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
比如:输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2
- 利用哈希表实现,但是需要O(n)的辅助空间
时间复杂度为O(n),空间复杂度为O(n)
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int,int> map;
for(auto n:nums)
{
map[n]++;
}
for(auto n:nums)
{
if(map[n]>(nums.size()>>1))
{
return n;
}
}
return -1;
}
}
- 基于Partition函数的时间复杂度为O(n)的算法
分析:这个数组的特性是数组中有一个数字出现的次数超过了数组长度的一半。如果把这个数组排序,那么排序之后位于数组中间的数字一定就是那个出现次数超过数组长度一半的数字。也就是说,这个数字是统计学上的中位数,即长度为n的数组中第n/2大的数字。也就是转化成了求第K大的数字问题,用Partition函数解决。这种方法会修改输入的数组。
当然,除了要完成基本功能还要考虑一些无效输入。
class Solution {
public:
//修改了数组,时间复杂度O(n),数据太多了会超时
int partition(vector<int>& arr,int s,int e)
{
int val=arr[s];
while(s < e)
{
while(s < e)
{
if(arr[e] < val)
{
arr[s++]=arr[e];
break;
}
e--;
}
while(s < e)
{
if(arr[s] > val)
{
arr[e--]=arr[s];
break;
}
s++;
}
}
arr[s]=val;
return s;
}
//检验数组中出现频率最高的数字有没有达到题目标准
bool Check(vector<int>& nums,int result)
{
int time=0;
for(int n : nums)
{
if(n == result)
time++;
}
if(time*2 > nums.size())
return true;
return false;
}
int majorityElement(vector<int>& nums) {
if(nums.size() <= 0)
return -1;
int s=0;
int e=nums.size()-1;
int m=(s+e)/2;
int pos=partition(nums,s,e);
while(pos != m)
{
if(pos > m)
{
e=pos-1;
pos=partition(nums,s,e);
}
else
{
s=pos+1;
pos=partition(nums,s,e);
}
}
int result=nums[m];
if(Check(nums,result))
return result;
return -1;
}
}
- 根据数组特点找出时间复杂度为O(n)的算法
考虑在遍历数组的时候保存两个值:一个是数组中的数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1,否则减1。如果次数为0,那么我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一个把次数设为1的数字。
class Solution {
public:
bool Check(vector<int>& nums,int result)
{
int time=0;
for(int n : nums)
{
if(n == result)
time++;
}
if(time*2 > nums.size())
return true;
return false;
}
int majorityElement(vector<int>& nums) {
if(nums.size() <= 0)
return -1;
int time=0;
int result=nums[0];
for(int n : nums)
{
if(time == 0)
{
result=n;
time++;
continue;
}
if(result == n)
{
time++;
}
else
{
time--;
}
}
if(Check(nums,result))
return result;
return -1;
}
};