摩尔投票法

229. 求众数 II

思路

方法一:哈希统计

用哈希统计数组中每个元素出现的次数。

方法二:摩尔投票法

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

假设投票是这样的,[A, C, A, A, B],ABC 是指三个候选人。

  • 第一张票与第二张票进行对坑,如果票不同则互相抵消掉;
  • 接着第三票与第四票进行对坑,如果票相同,则增加这个候选人的可抵消票数;
  • 这个候选人拿着可抵消票数与第五张票对坑,如果票不同,则互相抵消掉,即候选人的可抵消票数 -1。

摩尔投票法分为两个阶段:抵消阶段和计数阶段。

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

摩尔投票法经历两个阶段最多消耗 O(2n) 的时间,也属于 O(n) 的线性时间复杂度,另外空间复杂度也达到常量级。

本题是在任意多的候选人中,选出票数超过⌊ 1/3 ⌋的候选人。
这意味着最多只能有两位候选人,反证:若候选人数m>2,则 由每名候选人的最小票数 x>n/3,求和得候选人总票数>mx>3n/3=n,即候选人的总票数超过了所有投票数,矛盾。
可以这样理解,假设投票是这样的 [A, B, C, A, A, B, C],ABC 是指三个候选人。

  • 第 1 张票,第 2 张票和第3张票进行对坑,如果票都不同,则互相抵消掉;
  • 第 4 张票,第 5 张票和第 6 张票进行对坑,如果有部分相同,则累计增加他们的可抵消票数,如 [A, 2] 和 [B, 1];
  • 接着将 [A, 2] 和 [B, 1] 与第 7 张票进行对坑,如果票都没匹配上,则互相抵消掉,变成 [A, 1] 和 [B, 0] 。

代码

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        // 创建返回值
        List<Integer> res = new ArrayList<>();
        if (nums == null || nums.length == 0) return res;
        // 初始化两个候选人candidate,和他们的计票
        int cand1 = nums[0], count1 = 0;
        int cand2 = nums[0], count2 = 0;

        // 摩尔投票法,分为两个阶段:配对阶段和计数阶段
        // 配对阶段
        for (int num : nums) {
            // 投票
            if (cand1 == num) {
                count1++;
                continue;
            }
            if (cand2 == num) {
                count2++;
                continue;
            }

            // 第1个候选人配对
            if (count1 == 0) {
                cand1 = num;
                count1++;
                continue;
            }
            // 第2个候选人配对
            if (count2 == 0) {
                cand2 = num;
                count2++;
                continue;
            }

            count1--;
            count2--;
        }

        // 计数阶段
        // 找到了两个候选人之后,需要确定票数是否满足大于 N/3
        count1 = 0;
        count2 = 0;
        for (int num : nums) {
            if (cand1 == num) count1++;
            else if (cand2 == num) count2++;
        }

        if (count1 > nums.length / 3) res.add(cand1);
        if (count2 > nums.length / 3) res.add(cand2);

        return res;
    }
}
posted @ 2021-10-22 12:54  当康  阅读(2361)  评论(0编辑  收藏  举报