摩尔投票法可以用来寻找数组中的多数元素。
经典的摩尔投票法用来寻找数组中出现次数超过一半的元素,注意一定是出现次数超过一半,刚好等于一半是不能用这种算法的。如leetcode 169:
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:输入: [2,2,1,1,1,2,2]
输出: 2
一个数组中出现次数大于n/2的元素最多只有1个。考虑这样一种做法:每次选原数组中的两个不同的数抵消,这样,最后数组中只可能剩下1种数,或0个数,如果剩下1种数,那么再检验一下这个数就是要找的多数元素。如果用一个数组arr来存储当前未能抵消的元素,那么这个过程如下:
以 nums = {2,2,1,1,1,2,2}为例,开始时,arr = {}。
扫描到 2 :因为arr为空,直接放到arr中,原数组不变。 arr = {2}, nums = {2,2,1,1,1,2,2};
扫描到2 :与arr中的元素相同,因此也放到arr中,原数组不变。arr = {2,2}, nums = {2,2,1,1,1,2,2};
扫描到1:与arr中的元素不同,可以抵消1个,原数组也对应抵消1个2和1个1。arr = {2}, nums = {2,1,1,2,2};
扫描到1:与arr中的元素不同,可以抵消1个,原数组也对应抵消1个2和1个1。arr = {}, nums = {1,2,2};
扫描到1:因为arr为空,直接放到arr中,原数组不变。arr = {1}, nums = {1,2,2}。
扫描到2:与arr中的元素不同,可以抵消1个,原数组对应抵消1个1和1个2。arr = {}, nums = {2}。
扫描到2:arr为空,直接放到arr中,arr = {2}, nums = {2}。
所以,最后剩下的一种元素是2。最后统计2在原数组中的出现次数,确实大于n/2。那么就找到了多数元素。
观察这一过程,arr中存在的数最多只有1种,所以可以用两个变量来代替arr,majority: arr中的数,count: arr的长度,就可以写出摩尔投票法的代码:
扫描到数字i时,分三种情况:
(1) arr非空,并且i与arr中的数相同,即 i == majority, 那么 count++,对应arr长度++。
(2) arr是空集,那么 majority = i, count++。
(3) i 是与 arr中的数不同的数,那么相互抵消, count--, 对应 arr长度--。
class Solution {
public:
int majorityElement(vector& nums) {
int majority = INT32_MAX, count = 0;
for(int i : nums)
{
if(count > 0 && i == majority) count++;
else if(count == 0)
{
majority = i;
count++;
}
else count--;
}
return majority;
}
};
这里最后没有检验majority是否真的出现了超过n/2次,是因为题目中保证了多数元素一定存在,所以剩下的一定是多数元素。
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
示例 1:
输入: [3,2,3]
输出: [3]
示例 2:输入: [1,1,1,3,3,2,2,2]
输出: [1,2]
超过n/3次的元素最多只有2个,所以可以找到出现次数前二大的元素,再判断它们是否符合条件。
用摩尔投票法找出现次数前二大的元素,就需要两组 majority和count变量(隐含的是2个arr),做法如下:
扫描到数字i,分5种情况(互斥):
(1) arr1非空,且i与arr1中的数字majority1相同,那么count1++, 扫描下一个数。
(2) arr2非空,且i与arr2中的数字majority2相同,那么count2++, 扫描下一个数。
(3) arr1空,那么把 i 放到 arr1中, 即 majority1 = i, count1++,扫描下一个数。
(4) arr2空,那么把 i 放到 arr2中,即 majority2 = i, count++, 扫描下一个数。
(5) arr1和arr2都非空,且i与arr1和arr2中的数都不相同,那么将 arr1和arr2的长度都--,作为抵消。
最后检查 majority1和majority2是否满足 > n/3次的条件。
class Solution {
public:
//超过n/3次的元素最多只有2个
//用摩尔投票法找到出现次数前2多的元素,再判断这这两个元素的出现次数是否超过n/3。
vector majorityElement(vector& nums) {
int majority1 = INT32_MAX, majority2 = INT32_MAX, count1 = 0, count2 = 0;
for(int i : nums)
{
if(count1 > 0 && i == majority1) count1++;
else if(count2 > 0 && i == majority2) count2++;
else if(count1 == 0)
{
majority1 = i;
count1++;
}
else if(count2 == 0)
{
majority2 = i;
count2++;
}
else
{
count1--;
count2--;
}
}
count1 = 0, count2 = 0;
for(int i : nums)
{
if(i == majority1) count1++;
else if(i == majority2) count2++;
}
vector ans;
if(count1 > nums.size() / 3) ans.push_back(majority1);
if(count2 > nums.size() / 3) ans.push_back(majority2);
return ans;
}
};