摩尔投票(包含题目讲解)

题目描述一

这是 LeetCode 上的「169. 多数元素」,难度为「简单」。

Tag : 「哈希表」、「摩尔投票」

数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。

若没有,返回 -1 。请设计时间复杂度为 n、空间复杂度为1 的解决方案。

示例 1:

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

示例 2:

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

方法一:使用哈希表

class Solution {
    public int majorityElement(int[] nums) {
        int n = nums.length;
        Map<Integer, Integer> map = new HashMap<>();
        for (int x : nums) {
            map.put(x, map.getOrDefault(x, 0) + 1);
            if (map.get(x) > n / 2) return x;
        }
        return -1;
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

方法二:摩尔投票

摩尔投票 :在集合中寻找可能存在的多数元素,这一元素在输入的序列重复出现并占到了序列元素的一半以上;在第一遍遍历之后应该再进行一个遍历以统计第一次算法遍历的结果出现次数,确定其是否为众数;如果一个序列中没有占到多数的元素,那么第一次的结果就可能是无效的随机元素。

换句话说,每次将两个不同的元素进行「抵消」,如果最后有元素剩余,则「可能」为元素个数大于总数一半的那个。

具体的,我们定义一个变量 x 来保存那个可能为主要元素的值,cnt用来记录该值的出现次数。然后在遍历数组 nums过程中执行如下逻辑:

如果 cnt为0:说明之前出现过的x已经被抵消完了,更新一下x为当前值,出现次数为1:x = nums[i], cnt = 1;
如果cnt不为0:说明之前统计的x还没被抵消完,这是根据 nums[i]与x是否相等进行计算即可:cnt += nums[i] == x ? 1 : -1。
当处理完nums之后,我们得到了一个「可能」的主要元素。注意只是可能,因为我们在处理过程中只使用了x和cnt来记录,我们是无法确定最后剩下的x是经过多次抵消后剩余的主要元素,还是只是不存在主要元素的数组中的无效随机元素。

因此我们需要再进行一次遍历,检查这个「可能」的主要元素x的出现次数是否超过总数一半。

代码:

class Solution {
    public int majorityElement(int[] nums) {
        int n = nums.length;
        int x = -1, cnt = 0;
        for (int i : nums) {
            if (cnt == 0) {
                x = i;
                cnt = 1;
            } else {
                cnt += x == i ? 1 : -1;
            }
        }
        cnt = 0;
        for (int i : nums) if (x == i) cnt++;
        return cnt > n / 2 ? x : -1;
    }
}

题目描述二

这是 LeetCode 上的「229. 多数元素二」,难度为「中等」。

Tag : 「哈希表」、「摩尔投票」

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

请设计时间复杂度为 n、空间复杂度为1 的解决方案。
示例 1:

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

示例 2:

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

直接用摩尔投票

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        // 根据反证法可以证明最多有两个数大于n/3
        int n = nums.length;
        int a = -1, b = -1;         // a,b为当前可能计数>n/3的数
        int c1 = 0, c2 = 0;         // c1记录a的数量,c2记录b的数量
        for(int i : nums) {
            if(c1 != 0 && a == i)           // a之前出现过
                c1++;
            else if(c2 != 0 && b == i) 
                c2++;
            else if(c1 == 0) {          // a位置没有数
                c1++;
                a = i;
            } else if(c2 == 0) {        // b位置没有数
                c2++;
                b = i;
            } else {
                c1--;
                c2--;
            }
        }

        c1 = 0;
        c2 = 0;
        for(int i : nums) {
            if(i == a)
                c1++;
            else if(i == b)
                c2++;
        }
        List<Integer> ans = new ArrayList<>();
        if(c1 > n/3)
            ans.add(a);
        if(c2 > n/3)
            ans.add(b);
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值