【LeetCode】229. 求众数 II

127 阅读2分钟

「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战」。

题目

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

示例 1

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

示例 2

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

示例 3

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

进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。

题解

哈希表法

今天这道题让我们求解出现次数超过 ⌊ n/3 ⌋ 次的元素,最简单的方法就是使用哈希表,统计每个元素出现的次数,再看哪些大于 n/3 即可。

代码

时间复杂度:O(n)

空间复杂度:O(n)

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        List<Integer> ans = new ArrayList<>();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() > nums.length / 3) {
                ans.add(entry.getKey());
            }
        }
        return ans;
    }
}

摩尔投票法

摩尔投票法的正确性取决于其使用的抵消机制,我们假设数组总长度为 n ,假如有众数,其出现的次数必须超过 n/2,也就是最少为 n/2 + 1,那么剩余的非众数的数量为 n/2 -1,我们用众数去抵消非众数,总能保证最后的数量大于 1,而候选者中存储的就是可能的众数,最后,实际上它是不是众数,还是需要再统计一遍的。

通过上面的原理,我们可以扩展到寻找出现次数超过 n/3 的众数,这样的众数最多有两个,我们声明两个候选者及其对应的两个数量即可,同样地遍历数组,遇到新的数拿它与候选者进行抵消,直到最后遍历完成,两个候选者中存储的就是可能的众数,我们一样要再次遍历数组,统计出这两个候选者的出现的总次数才能确定它们是不是众数。

代码

时间复杂度:O(n)

空间复杂度:O(1)

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        List<Integer> ans = new ArrayList<>();
        int cand1 = 0, count1 = 0;
        int cand2 = 0, count2 = 0;
        for (int num : nums) {
            if (cand1 == num) {
                count1++;
            } else if (cand2 == num) {
                count2++;
            } else if (count1 == 0) {
                cand1 = num;
                count1 = 1;
            } else if (count2 == 0) {
                cand2 = num;
                count2 = 1;
            } else {
                count1--;
                count2--;
            }
        }
        count1 = count2 = 0;
        for (int num : nums) {
            if (cand1 == num) {
                count1++;
            } else if (cand2 == num) {
                count2++;
            }
        }
        if (count1 > nums.length / 3) ans.add(cand1);
        if (count2 > nums.length / 3) ans.add(cand2);
        return ans;
    }
}

结语

业精于勤,荒于嬉;行成于思,毁于随。