摩尔投票法 -leetcode-229. 求众数 II

面试题 17.10. 主要元素

题目描述

数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。若没有,返回 -1 。请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。

  • 本题和 169. 多数元素 一致。

摩尔投票法 -leetcode-229. 求众数 II_第1张图片
解法选择:

  • 法1:哈希表
  • 法2:排序
  • 法3:摩尔投票法 ⭐️

哈希

使用 map 统计每个元素出现的个数,最终保留出现次数大于 n / 2 n / 2 n/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) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

排序

如果将数组 nums 中的所有元素按照单调递增或单调递减的顺序排序,那么下标为 n u m s . l e n g t h / 2 nums.length / 2 nums.length/2 的元素(下标从 0 开始)一定是众数。

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length / 2];
    }
}
  • 时间复杂度: O ( n l o g n ) O(n log n) O(nlogn) (快排的时间复杂度)
  • 空间复杂度: O ( l o g n ) O(log n) O(logn) (快排利用 栈 的空间)

摩尔投票法 ⭐️

用 哈希表 来求解本题,不是最好的解法,因为其 空间复杂度为 O ( n ) O(n) O(n)

此类在一个集合中寻找 多数元素(或,众数)问题,使用 摩尔投票法 可以将 空间复杂度降为 O ( 1 ) O(1) O(1)

摩尔投票法 分为2个阶段:

变量定义:设 cand 为“可能”候选人(即,多数元素),countcand 的 “可抵消次数”

  • 投票阶段(抵消阶段):不相同的元素,则抵消;相同的元素,则累加;
    • count == 0 时,说明之前的候选人 c a n d cand cand 对应的 c o u n t count count 都被抵消完事了,此时需要更换候选人
    • 比如,给定数组为 [ 1 , 1 , 2 , 2 , 2 ] [1, 1, 2, 2, 2] [1,1,2,2,2],当 i = 4 i = 4 i=4 c o u n t = = 0 count== 0 count==0,此时需要更新 c a n d = n u m s [ 4 ] cand = nums[4] cand=nums[4]
  • 计数阶段最终得到的“可能” 候选者 cand,不一定是最终 的众数,所以还需要一轮 计算阶段,来判断后选择 cand 是不是最终的“众数”
    • 比如, [ 1 , 2 , 3 ] [1,2,3] [1,2,3] 得到的结果为 c a n d = 3 cand = 3 cand=3 c o u n t = 1 count = 1 count=1,但是其实这个 n u m s nums nums 中并没有 众数。所以,需要 计数阶段
class Solution {
    public int majorityElement(int[] nums) {
        int n = nums.length;
        int cand = -1;
        int count = 0;
        // 1、投票阶段
        for (int i : nums) {
            // 1.1 投票
            if (cand == i) { // 相同元素,则累加
                count++;
                continue;
            } 
            // 1.2 更换候选人
            if (count == 0) {
                cand  = i;
                count = 1;
                continue;
            }
            // 1.3 不同元素,则抵消
            count--;
        }
        // 2、计数阶段
        int sum = 0;
        for (int i : nums) {
            if (i == cand ) sum++;
        }
        if (sum > n / 2) return cand;
        else return -1;
    }
}
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

注意⚠️:“投票阶段”三者的顺序不可以改变,否则逻辑则会出现问题

  • 即 (1.1)累加判断 (1.2)更换候选人 (1.3)抵消

229. 求众数 II

题目描述

给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
摩尔投票法 -leetcode-229. 求众数 II_第2张图片

参考:题解-摩尔投票法

思路

  • 类似 面试题 17.10. 主要元素,仍然采用 摩尔投票法。不同之处在于,本题需要两个候选人 cand1cand2,整体代码和 面试题 17.10. 主要元素 板子基本一致。

总结:

  • 如果至多选 1 个代表,那他的票数至少要超过一半(⌊ 1/2 ⌋)的票数;
  • 如果至多选 2 个代表,那他们的票数至少要超过 ⌊ 1/3 ⌋ 的票数;
  • 如果至多选 m 个代表,那他们的票数至少要超过 ⌊ 1/(m+1) ⌋ 的票数。
class Solution {
    public List<Integer> majorityElement(int[] nums) {
        List<Integer> res = new ArrayList<>();
        int cand1 = -1;
        int count1 = 0;
        int cand2 = -1;
        int count2 = 0;
        // 1、投票阶段
        for (int num : nums) {
            // 1.1 投票
            if (num == cand1) { // 相同,则累加
                count1++;
                continue;
            }
            if (num == cand2) { // 相同,则累加
                count2++;
                continue;
            }
            // 1.2 更换候选人
            if (count1 == 0) {
                cand1 = num;
                count1 = 1;
                continue;
            }
            if (count2 == 0) {
                cand2 = num;
                count2 = 1;
                continue;
            }
            // 1.3 抵消: 当前元素和两个候选人都不同,则抵消
            count1--;
            count2--;
        }
        // 2、计数阶段
        int sum1 = 0;
        int sum2 = 0;
        for (int num : nums) {
            if (cand1 == num) sum1++;
            else if (cand2 == num) sum2++;
        }
        if (sum1 > nums.length / 3) {
            res.add(cand1);
        }
        if (sum2 > nums.length / 3) {
            res.add(cand2);
        }
        return res;
    }
}

注意⚠️:“投票阶段”三者的顺序不可以改变,否则逻辑则会出现问题

  • 即 (1.1)累加判断 (1.2)更换候选人 (1.3)抵消

笔记小记

  • start 2021/10/23
  • update 2022/4/23

你可能感兴趣的:(leetcode,leetcode,算法,java)