算法 - 摩尔投票法(Boyer-Moore majority vote algorithm)

数据结构 - 摩尔投票法(Boyer-Moore majority vote algorithm)

对应练习题目:

​ 初级:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/

​ 进阶:https://leetcode-cn.com/problems/majority-element-ii/

1. 摩尔投票法

摩尔投票算法也可以叫做多数投票算法,本文全部使用Java代码。时间复杂度为O(n),空间复杂度为O(1)。

核心就是对拼消耗。玩一个诸侯争霸的游戏,假设你方人口超过总人口一半以上,并且能保证每个人口出去干仗都能一对一同归于尽。最后还有人活下来的国家就是胜利。

那就大混战呗,最差所有人都联合起来对付你(对应你每次选择作为计数器的数都是众数),或者其他国家也会相互攻击(会选择其他数作为计数器的数),但是只要你们不要内斗,最后肯定你赢。最后能剩下的必定是自己人。

1. 实例一

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。你可以假设数组是非空的,并且给定的数组总是存在多数元素。

分析:

  1. res的票+1,非众数-1,根据题目的描述则一定有所有数字的票数和 > 0 ;
  2. 若数组的前 a 个数字的票数和 == 0 ,则数组剩余 (n-a) 个数字的票数和一定仍 > 0 ,即后 (n-a)个数字的最多数仍为 res。(严格来说是超过一半的数)
class Solution {
    public int majorityElement(int[] nums) {
        int res = 0, votes = 0, count = 0;
        for(int num : nums){
            if(votes == 0) res = num;//votes为0时 对res重新赋值
            votes += num == res ? 1 : -1;
        }
        // 验证 res 是否为众数
        for(int num : nums)
            if(num == res) count++;
        return count > nums.length / 2 ? x : 0; // 当无众数时返回 0
    }
}

每次从数组中找出一对不同的元素,将它们从数组中删除,直到遍历完整个数组。由于这道题已经说明一定存在一个出现次数超过一半的元素,所以遍历完数组后数组中一定会存在至少一个元素。

2. 实例二

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

**分析:**使用摩尔投票法,一次移动以三个数组为单位,所以到最后一定是会留下超过n/3的元素,且注意这里的元素一定是只有两个(三个大于n/3的数之和一定大于n,不符)!(这里就可以衍生到超过1/n次的元素的做法)。

public class Solution{
    public List<Integer> majorityElement(int[] nums) {
        int len = nums.length;
        List<Integer> list = new ArrayList<>(2);//初始化容量
        int count1 = 0,count2 = 0,count3 = 0,votes1 = 0,votes2 = 0,votes3 = 0,res1 = 0,res2 = 0,res3 = 0;//一次删除三个数 大于n/3的数只能是两个
        for(int num:nums){
            if(votes1 == 0) res1 = num;
            if(votes2 == 0) res2 = num;
            if(votes3 == 0) res3 = num;
            if(res1 == num) votes1++;
            else if(res2 == num) votes2++;//else 很有关键 它使得num1和num2能够分开
            else if(res3 == num) votes3++;
            else{
                votes1--;
                votes2--;
                votes3--;
            }
        }
        //验证
        for (int num:nums) {
            if(res1 == num) count1++;
            if(res2 == num) count2++;
            if(res3 == num) count3++;
        }
        if(count1 > len/3) list.add(res1);
        if(res1 != res2 && count2 > len/3) list.add(res2);
        if(res1 != res3 && res2 != res3 && count3 > len/3) list.add(res3);
        return list;
    }
}

3. 题外话

在LeetCode上面还有另外一种被推崇的解析,我认为它的continue容易误导人对数据结构在大脑中构建,看代码:

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        List<Integer> ans = new ArrayList<>();
        int aimLen = nums.length / 3;
        int res1 = 0, res2 = 0, count1 = 0, count2 = 0;
        for (int cur : nums) {
            if (cur == res1) {
                ++count1;
                continue;
            }
            if (cur == res2){
                ++count2;
                continue;
            }
            if (count1 == 0){
                res1 = cur;
                count1 = 1;
                continue;
            }
            if (count2 == 0){
                res2 = cur;
                count2 = 1;
                continue;
            }
            --count1;
            --count2;
        }
        count1 = 0;
        count2 = 0;
        for (int cur : nums){
            if (cur == res1) {
                ++count1;
                continue;
            }
            if (cur == res2) ++count2;
        }
        if (count1 > aimLen) ans.add(res1);
        if (count2 > aimLen) ans.add(res2);
        return ans;
    }
}

一次是判断三个数,上面的使用continue缺没有真正的做到一次判断三个数。具体解析看官网把…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Boyer-Moore投票是一种用于查找元素序列中的众数的算法,它具有线性时间复杂度和常数级空间复杂度。该算法由Robert S. Boyer和J Strother Moore命名,是一种典型的流算法。\[1\] Boyer-Moore投票分为两个阶段:抵消阶段和计数阶段。在抵消阶段,两个不同的候选人进行对抗,并同时抵消各自的一张票。如果两个候选人相同,则累加可抵消的次数。在计数阶段,如果抵消阶段最后得到的抵消计数不为0,那么这个候选人有可能超过一半的票数。为了验证这一点,需要遍历一次元素序列,统计票数,才能确定最终的众数。\[2\] 更多关于Boyer-Moore投票的详细信息可以参考\[3\]。 #### 引用[.reference_title] - *1* *3* [摩尔投票算法](https://blog.csdn.net/qq_17550379/article/details/83818965)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [摩尔(Boyer-Moore)投票](https://blog.csdn.net/qq_40692109/article/details/104805815)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值