该算法用来寻找一个数组中出现次数超过1/k的元素,易得满足要求的元素最多有k-1个。
摩尔投票法分为两个阶段:抵消阶段和计数阶段。
抵消阶段:两个不同投票进行对坑,并且同时抵消掉各一张票,如果两个投票相同,则累加可抵消的次数;
计数阶段:在抵消阶段最后得到的抵消计数只要不为0,那这个候选人是有可能超过一半的票数的,为了验证,则需要遍历一次,统计票数,才可确定。
我们通过下面两道题来深入理解这个算法:
1.(寻找众数)
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋
的元素。
这道题方法很多:
1.首先我们可以在平方级的时间里穷举所有情况,来检测每个数是不是众数。不过时间复杂度为O(n2)。
2.其次我们可以对数组排序,然后选择中间元素,即为众数。如果使用快速排序时间复杂度为O(nlogn)。
3.我们这里用摩尔投票法怎么做呢?
我们只需维护一个计数器,如果遇到一个我们目前的候选众数,就将计数器加一,否则减一。只要计数器等于 0 ,我们就将 nums 中之前访问的数字全部忘记 ,并把下一个数字当做候选的众数。可用你还没理解,我们换句话讲,每次从数组中拿走2个不同的数,最后剩下的一定是众数。这样只需要进行一次遍历,因此时间复杂度为O(n)。下面是c++实现:
class Solution {
public:
int majorityElement(vector<int>& nums) {
int count=1;
int mode=nums[0];
for(int i=1;i<nums.size();i++)
{
if(count==0)mode=nums[i];
if(nums[i]==mode)count++;
else count--;
}
return mode;
}
};
python实现:
class Solution:
def majorityElement(self, nums):
count = 0
mode = None
for num in nums:
if count == 0:
mode = num
count += (1 if num == mode else -1)
return mode
2.(求众数升级)
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋
次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
在假设A, B均为超过[n/3]的数的情况下, 数的分布情况如下所示
其中第一列全是A, 第二列全是B, 第三列为其他数, 且第三列即使所有数都一样, 也不会超过[n/3], 更何况可能存在不一样的数;
如果我每次都拿走3个不一样的数, 那么最后剩下的, 一定是A, B.
这里我们维护两个计数器count1和count2,维护两个初值不同的待返回元素num1和num2。遍历数组,若当前值和某个num相同,则对应count加一,若和两个num都不同,则两个count都减一;若某一时刻,某一个count减至0,则接下来重新给num赋值。遍历结束后统计num1和num2的个数,如果大于1/3则返回。c++实现如下:
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
int num1,num2,count1=0,count2=0;
vector<int>v;
for(int i=0;i<nums.size();++i)
{
if(nums[i]==num1)count1++;
else if(nums[i]==num2)count2++;
else if(count1==0){
count1++;
num1=nums[i];
}
else if(count2==0){
count2++;
num2=nums[i];
}
else{
count1--;
count2--;
}
}
//判断num1,num2数量是否大于1/3
count2=0; count1=0;
for(int x:nums)
{
if(x==num1)count1++; if(x==num2)count2++;
}
if(count1>nums.size()/3)v.push_back(num1);
if(count2>nums.size()/3)v.push_back(num2);
return v;
}
};
python实现:
def majorityElement(self, nums: List[int]) -> List[int]:
m = n = None
mcount = ncount = 0
for num in nums:
if num == m:
mcount += 1
elif num == n:
ncount += 1
elif not mcount:
m, mcount = num, 1
elif not ncount:
n, ncount = num, 1
else:
mcount -= 1
ncount -= 1
mcount = ncount = 0
for num in nums:
if num == m:
mcount += 1
elif num == n:
ncount += 1
N = len(nums)
return [i for i, c in zip((m, n), (mcount, ncount)) if c > N//3]