「这是我参与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;
}
}
结语
业精于勤,荒于嬉;行成于思,毁于随。