题目
思路一 哈希表
用一个哈希表存储数组中元素出现的次数,最后判断大于n/3的就放入要返回的vector中。
代码一
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
vector<int> ret;
unordered_map<int,int> cnt;
int n=nums.size();
for(int num:nums)
cnt[num]++;
for(auto v:cnt){
if(v.second>n/3)
ret.push_back(v.first);
}
return ret;
}
};
思路二 摩尔投票
摩尔投票法分为两个阶段:抵消阶段和计数阶段。
抵消阶段:两个不同投票进行对抗,并且同时抵消掉各一张票,如果两个投票相同,则累加可抵消的次数;
计数阶段:在抵消阶段最后得到的抵消计数只要不为 0,那这个候选人是有可能超过一半的票数的,为了验证,则需要遍历一次,统计票数,才可确定。
摩尔投票法经历两个阶段最多消耗 O(2n)的时间,也属于 O(n) 的线性时间复杂度,另外空间复杂度也达到常量级。
归纳:
如果至多选一个代表,那他的票数至少要超过一半(⌊ 1/2 ⌋)的票数;
如果至多选两个代表,那他们的票数至少要超过 ⌊ 1/3 ⌋ 的票数;
如果至多选m个代表,那他们的票数至少要超过 ⌊ 1/(m+1) ⌋ 的票数。
一个很好的理解:摩尔投票就是一个消消乐游戏,每次消除3个不同的数。因为我们想要的结果至少获得了 ⌊ 1/3 ⌋的票数,那么它一定没有被完全消除。通过消消乐可以缩小候选人的寻找范围。要验证结果就遍历一遍计算票数就好了。
对于这一道题,设置两个候选人和两个计数值。依然是有抵消和计数两个阶段。抵消阶段,先找到两个不同的候选人,再往后遍历。如果当前数能匹配到候选人,就给计数值加一。如果匹配不到,判断当前两个候选人的计数值。如果计数值都大于0,就将计数值减一;如果有一个计数值为0,就把这个候选人换掉,这个计数值改为1.
代码二
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
int cand1,cand2,cnt1,cnt2,n=nums.size();
//初始候选人任意,因为cnt为0会更新
cand1=cand2=cnt1=cnt2=0;
for(int num:nums){
if(num==cand1){
cnt1++;
}else if(num==cand2){
cnt2++;
}else if(cnt1&&cnt2){
cnt1--;
cnt2--;
}else if(!cnt1){
cand1=num;
cnt1=1;
}else if(!cnt2){
cand2=num;
cnt2=1;
}
}
//遍历判断结果是否正确
cnt1=cnt2=0;
for(int num:nums){
if(num==cand1)
cnt1++;
else if(num==cand2)
cnt2++;
}
vector<int> v;
if(cnt1>n/3) v.push_back(cand1);
if(cnt2>n/3) v.push_back(cand2);
return v;
}
};
ps.如果是找出现次数大于n/2的数,还可以通过排序,中位数就是中间的元素。