其他分享
首页 > 其他分享> > 摩尔投票法

摩尔投票法

作者:互联网

摩尔投票法(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的判断最后候选是否相同代码进行修改即可。

标签:cands,nums,int,摩尔,++,num,cnts,投票
来源: https://blog.csdn.net/GHR7719/article/details/116121545