摩尔投票法
229. 求众数 II
思路
方法一:哈希统计
用哈希统计数组中每个元素出现的次数。
方法二:摩尔投票法
摩尔投票法:摩尔投票法的核心思想为对拼消耗。首先我们考虑最基本的摩尔投票问题,比如找出一组数字序列中出现次数大于总数一半的数字(并且假设这个数字一定存在)。我们可以直接利用反证法证明这样的数字只可能有一个。
假设投票是这样的,[A, C, A, A, B],ABC 是指三个候选人。
- 第一张票与第二张票进行对坑,如果票不同则互相抵消掉;
- 接着第三票与第四票进行对坑,如果票相同,则增加这个候选人的可抵消票数;
- 这个候选人拿着可抵消票数与第五张票对坑,如果票不同,则互相抵消掉,即候选人的可抵消票数 -1。
摩尔投票法分为两个阶段:抵消阶段和计数阶段。
- 抵消阶段:两个不同投票进行对坑,并且同时抵消掉各一张票,如果两个投票相同,则累加可抵消的次数;
- 计数阶段:在抵消阶段最后得到的抵消计数只要不为 0,那这个候选人是有可能超过一半的票数的,为了验证,则需要遍历一次,统计票数,才可确定。
摩尔投票法经历两个阶段最多消耗 O(2n) 的时间,也属于 O(n) 的线性时间复杂度,另外空间复杂度也达到常量级。
本题是在任意多的候选人中,选出票数超过⌊ 1/3 ⌋的候选人。
这意味着最多只能有两位候选人,反证:若候选人数m>2,则 由每名候选人的最小票数 x>n/3,求和得候选人总票数>mx>3n/3=n,即候选人的总票数超过了所有投票数,矛盾。
可以这样理解,假设投票是这样的 [A, B, C, A, A, B, C],ABC 是指三个候选人。
- 第 1 张票,第 2 张票和第3张票进行对坑,如果票都不同,则互相抵消掉;
- 第 4 张票,第 5 张票和第 6 张票进行对坑,如果有部分相同,则累计增加他们的可抵消票数,如 [A, 2] 和 [B, 1];
- 接着将 [A, 2] 和 [B, 1] 与第 7 张票进行对坑,如果票都没匹配上,则互相抵消掉,变成 [A, 1] 和 [B, 0] 。
代码
class Solution {
public List<Integer> majorityElement(int[] nums) {
// 创建返回值
List<Integer> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
// 初始化两个候选人candidate,和他们的计票
int cand1 = nums[0], count1 = 0;
int cand2 = nums[0], count2 = 0;
// 摩尔投票法,分为两个阶段:配对阶段和计数阶段
// 配对阶段
for (int num : nums) {
// 投票
if (cand1 == num) {
count1++;
continue;
}
if (cand2 == num) {
count2++;
continue;
}
// 第1个候选人配对
if (count1 == 0) {
cand1 = num;
count1++;
continue;
}
// 第2个候选人配对
if (count2 == 0) {
cand2 = num;
count2++;
continue;
}
count1--;
count2--;
}
// 计数阶段
// 找到了两个候选人之后,需要确定票数是否满足大于 N/3
count1 = 0;
count2 = 0;
for (int num : nums) {
if (cand1 == num) count1++;
else if (cand2 == num) count2++;
}
if (count1 > nums.length / 3) res.add(cand1);
if (count2 > nums.length / 3) res.add(cand2);
return res;
}
}