摩尔投票法

摩尔投票法可以用来寻找数组中的多数元素。

经典的摩尔投票法用来寻找数组中出现次数超过一半的元素,注意一定是出现次数超过一半,刚好等于一半是不能用这种算法的。如leetcode 169:

1.169. 多数元素

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

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

 

示例 1:

输入: [3,2,3]
输出: 3
示例 2:

输入: [2,2,1,1,1,2,2]
输出: 2

一个数组中出现次数大于n/2的元素最多只有1个。考虑这样一种做法:每次选原数组中的两个不同的数抵消,这样,最后数组中只可能剩下1种数,或0个数,如果剩下1种数,那么再检验一下这个数就是要找的多数元素。如果用一个数组arr来存储当前未能抵消的元素,那么这个过程如下:

以 nums = {2,2,1,1,1,2,2}为例,开始时,arr = {}。

扫描到 2 :因为arr为空,直接放到arr中,原数组不变。 arr = {2}, nums = {2,2,1,1,1,2,2};

扫描到2 :与arr中的元素相同,因此也放到arr中,原数组不变。arr = {2,2}, nums =  {2,2,1,1,1,2,2};

扫描到1:与arr中的元素不同,可以抵消1个,原数组也对应抵消1个2和1个1。arr = {2}, nums = {2,1,1,2,2};

扫描到1:与arr中的元素不同,可以抵消1个,原数组也对应抵消1个2和1个1。arr = {}, nums = {1,2,2};

扫描到1:因为arr为空,直接放到arr中,原数组不变。arr = {1}, nums = {1,2,2}。

扫描到2:与arr中的元素不同,可以抵消1个,原数组对应抵消1个1和1个2。arr = {}, nums = {2}。

扫描到2:arr为空,直接放到arr中,arr = {2}, nums = {2}。

所以,最后剩下的一种元素是2。最后统计2在原数组中的出现次数,确实大于n/2。那么就找到了多数元素。

观察这一过程,arr中存在的数最多只有1种,所以可以用两个变量来代替arr,majority: arr中的数,count: arr的长度,就可以写出摩尔投票法的代码:

扫描到数字i时,分三种情况:

(1) arr非空,并且i与arr中的数相同,即 i == majority, 那么 count++,对应arr长度++。

(2) arr是空集,那么 majority = i, count++。

(3) i 是与 arr中的数不同的数,那么相互抵消, count--, 对应 arr长度--。

class Solution {
public:
    int majorityElement(vector& nums) {
        int majority = INT32_MAX, count = 0;
        for(int i : nums)
        {
            if(count > 0 && i == majority) count++;
            else if(count == 0)
            {
                majority = i;
                count++;
            }
            else count--;
        }
        return majority;
    }
};

这里最后没有检验majority是否真的出现了超过n/2次,是因为题目中保证了多数元素一定存在,所以剩下的一定是多数元素。

2. 229. 求众数 II

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

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

示例 1:

输入: [3,2,3]
输出: [3]
示例 2:

输入: [1,1,1,3,3,2,2,2]
输出: [1,2]

 超过n/3次的元素最多只有2个,所以可以找到出现次数前二大的元素,再判断它们是否符合条件。

用摩尔投票法找出现次数前二大的元素,就需要两组 majority和count变量(隐含的是2个arr),做法如下:

扫描到数字i,分5种情况(互斥):

(1) arr1非空,且i与arr1中的数字majority1相同,那么count1++, 扫描下一个数。

(2) arr2非空,且i与arr2中的数字majority2相同,那么count2++, 扫描下一个数。

(3) arr1空,那么把 i 放到 arr1中, 即 majority1 = i, count1++,扫描下一个数。

(4) arr2空,那么把 i 放到 arr2中,即 majority2 = i, count++, 扫描下一个数。

(5) arr1和arr2都非空,且i与arr1和arr2中的数都不相同,那么将 arr1和arr2的长度都--,作为抵消。

最后检查 majority1和majority2是否满足 > n/3次的条件。

class Solution {
public:
    //超过n/3次的元素最多只有2个
    //用摩尔投票法找到出现次数前2多的元素,再判断这这两个元素的出现次数是否超过n/3。
    vector majorityElement(vector& nums) {
        int majority1 = INT32_MAX, majority2 = INT32_MAX, count1 = 0, count2 = 0;
        for(int i : nums)
        {
            if(count1 > 0 && i == majority1) count1++;
            else if(count2 > 0 && i == majority2) count2++;
            else if(count1 == 0)
            {
                majority1 = i;
                count1++;
            }
            else if(count2 == 0)
            {
                majority2 = i;
                count2++;
            }
            else
            {
                count1--;
                count2--;
            }
        }
        count1 = 0, count2 = 0;
        for(int i : nums)
        {
            if(i == majority1) count1++;
            else if(i == majority2) count2++;
        }
        vector ans;
        if(count1 > nums.size() / 3) ans.push_back(majority1);
        if(count2 > nums.size() / 3) ans.push_back(majority2);
        return ans;
    }
};

 

你可能感兴趣的:(leetcode)