摩尔投票法(Boyer–Moore majority vote algorithm)出自论文
《MJRTYA Fast Ma jority
Vote Algorithm》
算法解决的问题是如何在任意多的候选人(选票无序),选出获得票数最多的那个。常见的算法是扫描一遍选票,对每个候选人进行统计的选票进行统计。当候选人的数目固定时,这个常见算法的时间复杂度为:O ( n ) ,当候选人的数目不定时,统计选票可能会执行较长时间,可能需运行O ( n^2 )
)的时间。当选票有序时,可以很容易编出O ( n ) 的程序,首先找到中位数,然后检查中位数的个数是否超过选票的一半。这篇论文针对无序且侯选人不定的情形,提出了摩尔投票算法。算法的比较次数最多是选票(记为n)的两倍,可以在O ( n )时间内选出获票最多的,空间开销为O ( 1 ) 。
形象化描述:
核心就是对拼消耗。
玩一个诸侯争霸的游戏,假设你方人口超过总人口一半以上,并且能保证每个人口出去干仗都能一对一同归于尽。最后还有人活下来的国家就是胜利。
混战时,可能所有人都联合起来对付你(对应你每次选择作为计数器的数都是众数),或者其他国家也会相互攻击(会选择其他数作为计数器的数),但是只要你们不要内斗,最后肯定你赢。
最后能剩下的必定是自己人。
算法应用
摩尔投票法的一大应用就是求众数。
相关题目有:
1
力扣169. 多数元素
class Solution {
public:
int majorityElement(vector<int>& nums) {
int n = nums.size();
int c = 1;
int num = nums[0];
for(int i = 1;i < n;i++)
{
if(num == nums[i])
c++;
else{
c--;
if(!c)
num = nums.at(i+1);
}
}
return num;
}
};
2
力扣 229. 求众数 II
可以看成这样一个情形:一个班里要选副班长,至多2位。每一个投一票,成为副班长得票必须超过总票数的三分之一。
因为可能会产生两名。所以候选人cand与计数cnt都转成相应的数组形式cands与cnts,长度都为2。
第一阶段成对抵销时,cands[0]与cands[1]的选票不相互抵销,即如果代表将票投给了cands[0],则cands[1]对应的cnts[1]的值不变化。
投给cands[1]也是同样的道理。这样就转化成摩尔投票法的原始情形了。
第二阶段计数时,除了要判断两个候选的票数是否超过三分之一,还需判断两个候选是否相同。
/*
时间复杂度为:O(n)
空间复杂度为:O(1)
*/
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
int len = nums.size();
vector<int>res, cands, cnts;
if(len == 0){//没有元素,直接返回空数组
return res;
}
cands.assign(2, nums[0]);
cnts.assign(2, 0);
//第1阶段 成对抵销
for(auto num: nums){
bool flag = false;
for(int i = 0; i < cands.size(); ++i){
if(num == cands[i]){
++cnts[i];
flag = true;
break;
}
}
if(!flag){
bool flag2 = false;
for(int j = 0; j < cands.size(); ++j){
if(cnts[j] == 0){
flag2 = true;
cands[j] = num;
cnts[j]++;
}
}
if(!flag2){
for(int j = 0; j < cnts.size(); ++j){
--cnts[j];
}
}
}
}
//第2阶段 计数 数目要超过三分之一
cnts[0] = cnts[1] = 0;
if(cands[0] == cands[1])
cands.pop_back();
for(auto num:nums){
for(int i = 0; i < cands.size(); ++i){
if(cands[i] == num){
++cnts[i];
break;
}
}
}
for(int i = 0; i < cands.size(); ++i){
if(cnts[i] > len / 3){
res.push_back(cands[i]);
}
}
return res;
}
};
应用 3
至多选出m个代表,每个选票数大于n / (m + 1)
只需要将题2的判断最后候选是否相同代码进行修改即可。