从0开始的秋招刷题路,记录下所刷每道题的题解,帮助自己回顾总结
求众数Ⅱ (摩尔投票法)
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
说明: 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。
示例 1:
输入:[3,2,3]
输出:[3]
示例 2:
输入:nums = [1]
输出:[1]
示例 3:
输入:[1,1,1,3,3,2,2,2]
输出:[1,2]
解法一:哈希。时间复杂度和空间复杂度超出要求。
class Solution {
public List<Integer> majorityElement(int[] nums) {
int n = nums.length;
HashMap<Integer, Integer> cnt = new HashMap<>();
List<Integer> ans = new ArrayList<>();
for(int num: nums)
cnt.put(num, cnt.getOrDefault(num, 0) + 1);
for(int num: cnt.keySet())
if(cnt.get(num) > n / 3)
ans.add(num);
return ans;
}
}
解法二:摩尔投票法。当题目要求求大于数组长度一半的元素时,我们可以用摩尔投票法得到该类型题目的基础版本。摩尔投票法可以想象成内拼消耗,遍历数组,如果遍历到的数字与候选数字一样,则认为是友军,将count加1,如果与候选数字不一样,则认为是敌军,先判断候选数字的count是否为0,若为0则相当于友军目前已经全部阵亡,将候选数字替换为遍历到的数字;若count不为0,则进行内拼消耗,将count减1,到最后剩下的那个候选数字,则是被认为可能的结果(需要遍历确认是否是实际的结果)。
class Solution {
public int majorityElement(int[] nums) {
int cand = nums[0], count = 0;
for(int num: nums){
// 配对
if(num == cand){
count++;
continue;
}
// 代替/消耗
if(count == 0){
cand = num;
count = 1;
continue;
}
else
count--;
}
// 题目说明了超过数组长度一半的数字必定存在,否则需要遍历判断是否大于n/2
// count = 0;
// for(int num: nums)
// if(cand == num)
// count++;
// if(count > n / 2)
// return cand;
// else
// return -1;
return cand;
}
}
当这个类型的题目扩展到求个数超过n / k的元素时,需要明白大前提为:**这个可能的结果最多存在k - 1个。**具体的求解思路跟基础版本一致,只是候选个数变为了k - 1个,先是候选数配对,如果有候选数配对上了,则将该候选数的count 加1,如果所有候选数都没有配对上,则开始代替/内拼消耗,**注意在这里代替只需要一个数字,而内拼消耗需要所有数字都减去1,这样保证了每个数字最多被消耗n / k次。**最后遍历数组,确认可能的结果是否为最终结果。
class Solution {
public List<Integer> majorityElement(int[] nums) {
int n = nums.length;
int cand1 = nums[0], cand2 = nums[0], count1 = 0, count2 = 0;
for(int num: nums){
// 配对
if(cand1 == num){
count1 ++;
continue;
}
if(cand2 == num){
count2 ++;
continue;
}
// 都没配对上,两对都抵消/换数字
if(count1 == 0){
cand1 = num;
count1 = 1;
continue;
}
if(count2 == 0){
cand2 = num;
count2 = 1;
continue;
}
count1--;
count2--;
}
// 找到的是有可能的数字,需要遍历确认是否大于n/3
count1 = 0;
count2 = 0;
for(int num: nums){
if(num == cand1)
count1++;
if(num == cand2)
count2++;
}
// 判断结果
List<Integer> ans = new ArrayList<>();
if(count1 > n / 3)
ans.add(cand1);
if(cand1 != cand2 && count2 > n / 3)
ans.add(cand2);
return ans;
}
}