题目:
Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋
times. The algorithm should run in linear time and in O(1) space.
分析:
针对“选择出现次数大于n/2的元素”的问题,每次舍弃两个不同的元素,最后剩下的元素有可能就是超过半数的那个。那么这道题同样可以采用这样的思路,每次舍弃三个互不相同的元素,那么剩下的元素有可能是出现次数超过n/3的那个。
1、这个思路类似于将n个元素分组,依次选择三个互不相同的元素组成一组(如果存在的话),那么可以分成(n+1)/3组,最后一组的元素个数可能小于3个,显然,出现次数超过n/3的元素肯定出现在最后一组中(抽屉原理)。
2、最后一组中的元素不一定都出现超过n/3次。
3、出现次数超过n/3的元素最多两个
代码维护两个不相等的候选值(可能出现次数超过n/3次)和与它们对应的计数器,考察每个新元素,这个新元素有可能被选为候选值也有可能作为“三个互不相等的元素”被舍弃
1、两个候选值都存在
比较两个候选值与新元素,三者都不相等,那么舍弃这三个元素(计数器减一);
如果新元素与其中一个候选值相等,那么增加对应的计数器
2、两个候选值不都存在(计数器为0)
如果新元素和某个存在的候选值相等,那么增加对应的候选值;
如果没有与新元素相等的候选值,那么将新元素选为候选值;
如果两个候选值都不存在,那么直接更新候选值。
class Solution {
private:
bool check(vector<int>& nums, int val)
{
int count = 0;
for (int i = 0; i < nums.size(); ++i)
{
if (val == nums[i]) ++count;
}
return count > nums.size() / 3;
}
public:
vector<int> majorityElement(vector<int>& nums) {
//每次删除三个不相同的元素,最后剩下的数字就是结果
vector<int> res;
if (nums.empty())
{
return res;
}
int num1, num2, count1 = 0, count2 = 0;
for (int i = 0; i < nums.size(); ++i)
{
if (count1 == 0 || count2 == 0)
{
if (count1 == 0 && count2 == 0)
{
num1 = nums[i];
++count1;
}
else if (count1 == 0) //count2肯定不是0
{
if (num2 == nums[i])
{
++count2;
}
else
{
num1 = nums[i];
++count1;
}
}
else //count1 != 0
{
if (num1 == nums[i])
{
++count1;
}
else
{
num2 = nums[i];
++count2;
}
}
}
else if (nums[i] != num1 && nums[i] != num2)
{
//删除三个不同的元素
--count1;
--count2;
}
else if (nums[i] == num1)
{
++count1;
}
else
{
++count2;
}
}
if (count1 != 0 && check(nums, num1)) res.push_back(num1);
if (count2 != 0 && check(nums, num2)) res.push_back(num2);
return res;
}
};
因为候选值不一定就是出现次数大于n/3的元素,因此需要check()。