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;
}
}