摩尔(Boyer-Moore)投票法

该算法用来寻找一个数组中出现次数超过1/k的元素,易得满足要求的元素最多有k-1个。

摩尔投票法分为两个阶段:抵消阶段和计数阶段。
抵消阶段:两个不同投票进行对坑,并且同时抵消掉各一张票,如果两个投票相同,则累加可抵消的次数;
计数阶段:在抵消阶段最后得到的抵消计数只要不为0,那这个候选人是有可能超过一半的票数的,为了验证,则需要遍历一次,统计票数,才可确定。

我们通过下面两道题来深入理解这个算法:

1.(寻找众数)

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

这道题方法很多:

1.首先我们可以在平方级的时间里穷举所有情况,来检测每个数是不是众数。不过时间复杂度为O(n2)。

2.其次我们可以对数组排序,然后选择中间元素,即为众数。如果使用快速排序时间复杂度为O(nlogn)。

3.我们这里用摩尔投票法怎么做呢?

我们只需维护一个计数器,如果遇到一个我们目前的候选众数,就将计数器加一,否则减一。只要计数器等于 0 ,我们就将 nums 中之前访问的数字全部忘记 ,并把下一个数字当做候选的众数。可用你还没理解,我们换句话讲,每次从数组中拿走2个不同的数,最后剩下的一定是众数。这样只需要进行一次遍历,因此时间复杂度为O(n)。下面是c++实现:

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int count=1;
        int mode=nums[0];
        for(int i=1;i<nums.size();i++)
        {
            if(count==0)mode=nums[i];
            if(nums[i]==mode)count++;
            else count--;
        }
        return mode;
    }
};

python实现:

class Solution:
    def majorityElement(self, nums):
        count = 0
        mode = None
        for num in nums:
            if count == 0:
                mode = num
            count += (1 if num == mode else -1)
        return mode

2.(求众数升级)

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

说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。

在假设A, B均为超过[n/3]的数的情况下, 数的分布情况如下所示

其中第一列全是A, 第二列全是B, 第三列为其他数, 且第三列即使所有数都一样, 也不会超过[n/3], 更何况可能存在不一样的数;
如果我每次都拿走3个不一样的数, 那么最后剩下的, 一定是A, B.

这里我们维护两个计数器count1和count2,维护两个初值不同的待返回元素num1和num2。遍历数组,若当前值和某个num相同,则对应count加一,若和两个num都不同,则两个count都减一;若某一时刻,某一个count减至0,则接下来重新给num赋值。遍历结束后统计num1和num2的个数,如果大于1/3则返回。c++实现如下:

class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {
        int num1,num2,count1=0,count2=0;
        vector<int>v;
        for(int i=0;i<nums.size();++i)
        {
            if(nums[i]==num1)count1++;
            else if(nums[i]==num2)count2++;
            else if(count1==0){
                count1++;
                num1=nums[i];
            }
            else if(count2==0){
                count2++;
                num2=nums[i];
            }
            else{
                count1--;
                count2--;
            } 
        }
        //判断num1,num2数量是否大于1/3
        count2=0;    count1=0;
        for(int x:nums)
        {
            if(x==num1)count1++;  if(x==num2)count2++;
        }
        if(count1>nums.size()/3)v.push_back(num1);
        if(count2>nums.size()/3)v.push_back(num2);
        return v;
    }
};

python实现:

    def majorityElement(self, nums: List[int]) -> List[int]:
        m = n = None
        mcount = ncount = 0
        for num in nums:
            if num == m:
                mcount += 1
            elif num == n:
                ncount += 1
            elif not mcount:
                m, mcount = num, 1
            elif not ncount:
                n, ncount = num, 1
            else:
                mcount -= 1
                ncount -= 1
        mcount = ncount = 0
        for num in nums:
            if num == m:
                mcount += 1
            elif num == n:
                ncount += 1
        N = len(nums)
        return [i for i, c in zip((m, n), (mcount, ncount)) if c > N//3]
发布了57 篇原创文章 · 获赞 260 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_40692109/article/details/104805815
今日推荐