Boyer-Moore majority vote algorithm (摩尔投票算法)
典型案例:
(1) 找出大于n/2的元素,leetcode: https://leetcode.com/problems/majority-element/
class Solution { public int majorityElement(int[] nums) { int count = 0; int cur = 0; for(int i = 0; i < nums.length; ++i){ if(count == 0){ cur = nums[i]; count++; }else{ if(nums[i] == cur){ count++; }else{ count--; } } } return cur; } }
(2) 找出大于n/3的元素, letcode: https://leetcode.com/problems/majority-element-ii/
/* 思路:摩尔投票升级版,超过n/3的数最多只能有两个; 先选出两个候选人A,B,遍历数组,如果投A(等于A),则A的票数++;如果投B,B的票数++; 如果A,B都不投(即与A,B都不相等),那么检查此时是否AB中候选人的票数是否为0,如果为0,则成为新的候选人; 如果A,B两个人的票数都不为0,那么A,B两个候选人的票数均--; 遍历结束后选出两个候选人,但是这两个候选人是否满足>n/3,还需要再遍历一遍数组,找出两个候选人的具体票数 */ public List<Integer> majorityElement(int[] nums) { if (nums==null||nums.length==0){ return null; } //初始化,定义两个候选人以及对应的票数 int candidateA=nums[0]; int candidateB=nums[0]; int countA=0; int countB=0; // 遍历数组 for (int num:nums){ if (num==candidateA){ //投A countA++; continue; } if (num==candidateB){// 投B countB++; continue; } //此时A,B都不投,检查是否有票数为0情况,如果为0,则更新候选人 if (countA==0){ candidateA=num; countA++; continue; } if (countB==0){ candidateB=num; countB++; continue; } //此时两个候选人的票数都大于1,且当前选名不投AB,那么A,B对应的票数都要--; countA--; countB--; } // 上一轮遍历找出了两个候选人,但是这两个候选人是否均满足票数大于N/3仍然没法确定,需要重新遍历,确定票数 countA=0; countB=0; for (int num:nums){ if (num==candidateA){ countA++; }else if (num==candidateB){ countB++; } } List<Integer> resultList=new ArrayList<>(); if (countA>nums.length/3){ resultList.add(candidateA); } if (countB>nums.length/3){ resultList.add(candidateB); } return resultList; }
Boyer-Moore majority vote algorithm(摩尔投票算法)是一种在线性时间O(n)和空间复杂度的情况下,在一个元素序列中查找包含最多的元素。它是以Robert S.Boyer和J Strother Moore命名的,1981年发明的,是一种典型的流算法(streaming algorithm)。
在它最简单的形式就是,查找最多的元素,也就是在输入中重复出现超过一半以上(n/2)的元素。如果序列中没有最多的元素,算法不能检测到正确结果,将输出其中的一个元素之一。
当元素重复的次数比较小的时候,对于流算法不能在小于线性空间的情况下查找频率最高的元素。
假设这个数组中共有n个元素,我们可以把数值不同的元素看做不同的群体成员(一个数字代表一个群体)。那么我们现在要根据每个群在这个名单中各自的人数,使得在名单中出现人数最多的那个管理群。我们就先从数组的第一个元素开始,假定它代表的群体的人数是最多的,那么根据数组中出现次数超过一半的数只有一个的特质,如果我们设置一个计数器,在遍历时遇到不同于这个群体的人时就将计数器-1,遇到同个群体的人时就+1,只要在计数器归0时就重新假定当前元素代表的群体为人数最多的群体再继续遍历(此时数据被分为两段,前一段数据中被计数的元素数和numbers except it 数量是相等的,而后面的data中又满足词频最高的数大于总数一半的情形,有点分治策略的思想),那么到了最后,计数器记录的那个群体必定是人最多的那个群体。这里就使得元素排序是不会造成任何影响的,只关心元素的个数所带来的对于计数器+1或-1的影响。
算法实现:
法在局部变量中定义一个序列元素(m)和一个计数器(i),初始化的情况下计数器为0. 算法依次扫描序列中的元素,当处理元素x的时候,如果计数器为0,那么将x赋值给m,然后将计数器(i)设置为1,如果计数器不为0,那么将序列元素m和x比较,如果相等,那么计数器加1,如果不等,那么计数器减1。处理之后,最后存储的序列元素(m),就是这个序列中最多的元素。
典型的案例
(1)找出大于n/2的元素
(2)找出大于n/3的元素把两个数转化为一个数的思想,两个大于n/3的肯定大于一半
---------------------
作者:码农张学友
来源:CSDN
原文:https://blog.csdn.net/dpengwang/article/details/81710259
版权声明:本文为博主原创文章,转载请附上博文链接!