问题引入:
下面先给出一个情景,假设你要从数组中选出众数,你会怎么做?
相信大部分人都第一时间想到新开一个数组开始统计计数然后输出计数器最大的那个值,如果我们限定新开数组,要求空间复杂度为O(1),并且时间复杂度为O(n)的做法呢?我们是不是就要思考一下了?
首先,不能新开空间,那么只能在原数组上动手脚了,但是,一旦将改变了原数组的值,就会影响众数的选择结果,所以只能在计数器上动手脚,众数之所以为众数,就是因为其数量大于其他数字(这里只考虑只有一个众数的情况,也即众数个数大于n/2个),倘若将众数看做一类,将其他的非众数数字都看做另一类,设置一个计数器和一个临时变量,计数器的作用是统计当前与临时变量中的数字相等的个数,每当遍历到一个不是正确众数的数字,就会将计数器减去一,直到计时器减到0,就将临时变量赋成新的遍历到的数,继续上面的操作;
在Boyer-Moore 投票算法中,遇到相同的数则将count 加 1,遇到不同的数则将count 减 1。
根据主要元素的定义,主要元素的出现次数大于其他元素的出现次数之和。
因此在遍历过程中,主要元素和其他元素两两抵消,如果存在主要元素的话,最后一定剩下至少一个主要元素。
下面以leetcode上的一道典型例题为例:
int majorityElement(int* nums, int numsSize){
int count = 0;
int candidate = -1;//任意值即可
for(int i=0;i<numsSize;i++)
{
if(nums[i]==candidate)//如果遍历到相同的数,计数器++
count++;
else
{
--count;//没有遍历到与candidate相等的元素,计数器要--以抵消一个主要元素
if(count<0)//如果计数器减到了或者第一次开始遍历,说明前面遍历的元素主要元素与非主要元素正好抵消了,重新开始计数,题目保证主要元素一定存在
{
candidate=nums[i];
count=1;//开始计数新的元素
}
}
}
return candidate;
}
从原理上我们可以知道,该算法并不适用于众数不唯一的情况,那么针对与一般情况更推荐采用哈希表法进行比较,创建哈希表用来统计数字出现次数,
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int,int> m;
int maxn=-1,count=0;//其中maxn用来记录当前最多出现的数,count用来记录次数
for(auto num:nums)
{
++m[num];
if(m[num]>count)//如果该数出现次数比count大了,就更新
{
maxn=num;
count=m[num];
}
}
return maxn;
}
};