概述
摩尔投票是在一次遍历、常量空间下求数组多数元素的算法,先来看这道题:
给定一个大小为 n
的数组 nums
,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋
的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3] 输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2] 输出:2
思路
*注意*:如果数组中某个数存在出现次数占数组的一半以上,我们称之为众数(这与数学定义不同)
想想这样一个事:
数组中某个数存在出现次数占数组的一半以上,那么它与其他的非众数一一抵消,肯定还会剩至少一个众数没被抵消。
那如果非众数内部也相互抵消呢?那剩的众数就更多了。
算法过程
我们有一个衡量标准votes,初始值为0,又有众数x,表示当前认为的众数是x。
*注意*:x不一定是全局众数,它只代表遍历到此位置时我们对众数的推断。
如果votes==0,那说明前面的数全抵消掉了,那么让x得到当前遍历位置的值。
如果当前的元素==x,votes+1,否则-1,这是在模拟抵消的过程。
x的最终值就是众数。
因为:数组中某个数存在出现次数占数组的一半以上,那么它与其他的非众数一一抵消,肯定还会剩至少一个众数没被抵消。
我们为什么可以在不完全遍历时推断众数呢?
因为:如果非众数内部也相互抵消,那剩的众数就更多了。我们选取局部假众数实际就是在进行这个过程。
例如,
nums[i] 1 2 2 3 4 4 4 4 4 5 4
i 0 1 2 3 4 5 6 7 8 9 10
一开始认为1是众数,1被抵消后认为2是众数,2被抵消后认为4是众数,在这个过程发生的有:
真众数与所有非真众数抵消,假众数与前面的数抵消。
理想情况下只有真众数与其他数抵消,它尚能剩余,更何况其他数之间的内部抵消也存在。
Code
class Solution {
public:
int majorityElement(vector<int>& nums) {
int x,votes=0;
for(const int& i:nums){
if(votes==0)x=i;
votes+=(i==x?1:-1);
}
return x;
}
};