该算法用于解决寻找一个含有 n n 个元素的数列中出现超过 1k 1 k (即大于 nk n k 次)的元素(假设满足要求的元素存在)。可知,满足要求的元素最多有 (k−1) ( k − 1 ) 个。
使用暴力解法并不难,但是摩尔投票法给出了一个 O(n) O ( n ) 时间复杂度的解法。
当k=2时
维护一个初值为0的计数器counter,维护一个任意初值的待返回元素result。遍历 {an} { a n } ,执行以下操作:若当前值和result相同,则counter加一,否则减一;当counter减至0时,result更换为当前元素。遍历结束时,返回result。
int majorityElement(vector<int>& nums) {
int res = 0, cnt = 0;
for (int num : nums) {
if (cnt == 0) {res = num; ++cnt;}
else (num == res) ? ++cnt : --cnt;
}
return res;
}
计正确答案应为r。
首先,如果counter从未减至0,则显然
a0
a
0
出现的次数超过
12
1
2
。
如果遍历完前2m项后,counter首次减至0,则
{an}
{
a
n
}
的前2m项中r出现次数不大于m,所以后
(n−2m)
(
n
−
2
m
)
项中r出现必然多于
12
1
2
。所以对后
(n−2m)
(
n
−
2
m
)
项进行摩尔投票,必然可以得到正确结果r。
当k=3时
维护两个初值为0的计数器counter1和counter2,维护两个初值不同的待返回元素result1和result2。遍历 {an} { a n } ,执行以下操作:若当前值和某个result相同,则对应counter加一,若和两个result都不同,则两个counter都减一;若某一时刻,某一个counter减至0,则result更换为当前元素;若某一时刻两个result都是0,则对后面的元素进行摩尔投票。遍历结束后,返回result1和result2中出现次数超过 13 1 3 的一个或者两个。
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
if(nums.size()<2)return nums;
int cddt1=0,cddt2=1;
int counter1=0,counter2=0;
for(int i=0;i<nums.size();i++){
if(cddt1==nums[i])counter1++;
else if(cddt2==nums[i])counter2++;
else if(counter1==0){cddt1=nums[i];counter1++;}
else if(counter2==0){cddt2=nums[i];counter2++;}
else {counter1--;counter2--;}
}
counter1=0;
counter2=0;
for(int i=0;i<nums.size();i++){
if(cddt1==nums[i])counter1++;
else if(cddt2==nums[i])counter2++;
}
vector<int> v;
if(counter1>nums.size()/3&&counter1!=0)v.push_back(cddt1);
if(counter2>nums.size()/3&&counter2!=0)v.push_back(cddt2);
return v;
}
};
先找出n个数中出现次数最多的两个数
r1
r
1
、
r2
r
2
,则两数中出现次数超过
13
1
3
的一个或者两个即为所求结果。
同上,首先,如果counter从未减至0,则显然出现没有其他数字的出现次数多于
a0、a1
a
0
、
a
1
。
如果遍历完前
(3m+t)
(
3
m
+
t
)
项后,某counter首次减至0,另一counter还是
t
t
,不妨将这个result并入后
(n−3m−t)
(
n
−
3
m
−
t
)
项,则剩余
3m
3
m
项中r1,r2出现次数不大于
m
m
,所以归并后的项中r1、r2出现必然多于
13
1
3
。所以对
(n−3m)
(
n
−
3
m
)
项(由之前归并的
t
t
个result和原数组的后)进行摩尔投票,必然可以得到正确结果
r1
r
1
、
r2
r
2
。
当k>3时
同理,维护 (k−1) ( k − 1 ) 个result和counter,找出出现次数最多的 (k−1) ( k − 1 ) 个元素,最后分别计算出现次数,返回满足要求的元素即可。