摩尔投票

71 阅读3分钟

摩尔投票法

229. 求众数 II

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

示例 1:

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

示例 2:

输入:nums = [1]
输出:[1]

示例 3:

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

背景知识

摩尔投票法:摩尔投票法的核心思想为对拼消耗。首先我们考虑最基本的摩尔投票问题,比如找出一组数字序列中出现次数大于总数 n2`\frac{n}{2}`的数字(并且假设这个数字一定存在)。我们可以直接利用反证法证明这样的数字只可能有一个。摩尔投票算法的核心思想是基于这个事实:

  • 每次从序列里选择两个不相同的数字删除掉(或称为「抵消」),最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个元素。假设我们当前数组中存在次数大于总数一半的元素为 x,数组的总长度为 n,则我们可以把数组分为两部分,一部分为相同的k 个元素x,另一部分为nk2`\frac{n-k}{2}`对个不同的元素配对,此时我们假设还存在另外一个次数大于总数一半的元素 y,则此时 y 因该满足y>n2`y>\frac{n}{2}`,但是按照我们之前的推理 y 应当满足ynk2`y\leqslant \frac{n-k}{2}`,二者自相矛盾。

解题思路

题目要求找出其中所有出现超过 n3`\frac{n}{3}` 次的元素。我们可以利用反证法推断出满足这样条件的元素最多只有两个,我们可以利用摩尔投票法的核心思想,每次选择三个互不相同的元素进行删除(或称为「抵消」)。我们可以假设数组中一定只存在一个次数大于 n3`\frac{n}{3}` 的元素x,其中n为数组的长度,则此时我们可以把数组分成两部分:一部分相同的k个元素x,另一部分为nk3`\frac{n-k}{3}`组三个不同的元素,我们知道三个不同的元素会被抵消,因此最终只会剩下一个元素为x。如果只存在2个次数大于n3`\frac{n}{3}`的元素时,我们假设这两个不同的元素分别为xy,则此时我们一定可以把数组分成三部分:第一部分相同的m个元素x,第二部分相同的 n个元素 y,第三部分为nxy3`\frac{n-x-y}{3}`组三个互不同的元素,我们知道三个互不同的元素会被抵消,因此最终只会剩下两个元素为 xy

  • 我们每次检测当前元素是否为第一个选中的元素或者第二个选中的元素。
  • 每次我们发现当前元素与已经选中的两个元素都不相同,则进行抵消一次。
  • 如果存在最终选票大于0 的元素,我们还需要再次统计已选中元素的次数,检查元素的次数是否大于 n3`⌊\frac{n}{3}⌋`
class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {
        vector<int> ans;
        int element1 = 0;
        int element2 = 0;
        int vote1 = 0;
        int vote2 = 0;

        for (auto & num : nums) {
            if (vote1 > 0 && num == element1) { //如果该元素为第一个元素,则计数加1
                vote1++;
            } else if (vote2 > 0 && num == element2) { //如果该元素为第二个元素,则计数加1
                vote2++;
            } else if (vote1 == 0) { // 选择第一个元素
                element1 = num;
                vote1++;
            } else if (vote2 == 0) { // 选择第二个元素
                element2 = num;
                vote2++;
            } else { //如果三个元素均不相同,则相互抵消1次
                vote1--;
                vote2--;
            }
        }

        int cnt1 = 0;
        int cnt2 = 0;
        for (auto & num : nums) {
            if (vote1 > 0 && num == element1) {
                cnt1++;
            }
            if (vote2 > 0 && num == element2) {
                cnt2++;
            }
        }
        // 检测元素出现的次数是否满足要求
        if (vote1 > 0 && cnt1 > nums.size() / 3) {
            ans.push_back(element1);
        }
        if (vote2 > 0 && cnt2 > nums.size() / 3) {
            ans.push_back(element2);
        }

        return ans;
    }
};

作者:LeetCode-Solution 链接:leetcode-cn.com/problems/ma… 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。