小小算法,可笑可笑——摩尔投票法(集万家之长)

摩尔投票法

不多说,先上题目。

问题描述:leetcode 229题
给定一个大小为 n 的整数数组,找出其中的所有的出现超过 ⌊ n/3 ⌋ 次的元素。

这还不简单!直接暴力计数,上map,easy搞定。
sorry!还有附加条件,忘记码上了>_<

设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。

这下全了>^<如何解呢?空间复杂度O(n)怎么办?
别急,先来个中断,在小本本上记下,咱们先调用摩尔投票法。

摩尔投票法

又叫多数投票算法(Boyer–Moore majority vote algorithm)
出自这篇论文,有兴趣的小伙伴可以看看,锻炼英语。。
这里就用汉语描述一下主要内容。

  • 算法描述

摩尔投票法,算法解决的问题是如何在任意多的候选人(选票无序),选出获得票数最多的那个。在无序且侯选人不定的情形,可运用摩尔投票法,投出一个主席,只要候选人的票数多余一半,就当选。
算法的比较次数最多是选票(记为n)的两倍,可以在 O(n)时间内选出获票最多的,空间开销为O(1)。

  • 算法步骤

算法分为两个阶段:pairing阶段和counting阶段。
pairing阶段(抵消阶段):两个不同选票的人进行对抗,并会同时击倒对方,当剩下的人都是同一阵营,相同选票时,结束。

counting阶段(计数阶段):对最后剩下的下进行选票计算统计,判断选票是否超过总票数的一半,选票是否有效。

这里是我觉得讲的最清楚的算法步骤的一篇文章,没有之一,自愧不如,就分享来给大家看看。把候选人想成数组也太妙了。

怎么办还不懂?

这里有大佬的动画演示,看完秒懂

这个柱状图角度也蛮新颖的

看完一圈回来,相信大家都明白摩尔投票法是什么了。那最初这个题也会解了吧。
这里只给出C++的代码实现

class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {
        vector<int> ans;
        if (nums.empty()) return ans;
        int cand1, cand2;
        int count1, count2;
        cand1 = cand2 = count1 = count2 = 0;
        // 抵消阶段
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] == cand1) count1++;
            else if (nums[i] == cand2) count2++;
            else if (count1 && count2) {
                count1--;
                count2--;
            }
            else if (!count1) {
                cand1 = nums[i];
                count1++;
            }
            else {
                cand2 = nums[i];
                count2++;
            }
        }
        // 计数阶段
        count1 = count2 = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] == cand1) count1++;
            else if(nums[i] == cand2) count2++; // 必须用else if,保证每个数只算到一个候选人头上
            else {}
        }
        if (count1 > nums.size() / 3) ans.push_back(cand1);
        if (count2 > nums.size() / 3) ans.push_back(cand2);
        return ans;
    }
};

最后总结一下:

1.摩尔投票法的第一步pairing是用来在O(n)时间和O(1)空间内,选出众数来。
2.因为是选出众数,例如在[1,2,3]中,1或2或3都是众数,因此,还需要第二不counting重新再遍历一边,统计选出的的票数是否多余1/2。
3.由此可以推广,如果选出两名主席,则pairing步骤需要找出两个众数,counting需要检查,每个选出来个的主席是否票数都多余1/3。
如果要选出m名主席,要选出m个众的数,检查是否都多余1/(m+1)。
4.还有什么???没了。。。哦对!还有其他解法。
比如:哈希表方法和排序法找中位数(这个只使用选出一个主席的情况哦)
哈希map:

class Solution {
public:
     int majorityElement(vector<int>& nums) {
        unordered_map<int, int> counts; 
        int n = nums.size();
        for (int i = 0; i < n; i++)
            if (++counts[nums[i]] > n / 2)
                return nums[i];
        return 0;
    }
};

排序:

int majorityElement(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        return nums[nums.size() / 2];
    } 
参考文章

1.多数投票算法(Boyer-Moore Algorithm)详解
2.摩尔投票法(Boyer–Moore majority vote algorithm)
3.Boyer–Moore majority vote algorithm

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值