题意
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
题解
常见的有三种解法
- 哈希表统计法:遍历数组,用map来统计各数字的数量,即可找出众数,时间复杂度和空间复杂度均为 O ( n ) O(n) O(n)
- 数组排序法:将数组排序,数组中点元素一定为众数
- 摩尔投票法:核心理念为票数正负相抵,此方法时间复杂度和空间复杂度分别为 O ( n ) O(n) O(n)和 O ( 1 ) O(1) O(1)
摩尔投票法
假设数组的众数为x,数组长度为n
简单理解就是,对众数投的票为+1
,对其他数投的票为-1
众数出现的次数超过了数组长度的一半,因此如果遍历数组进行投票,最终的票数之和肯定大于0,而且如果前 m
个元素票数之和等于 0
,数组后 n-m
个元素的票数肯定大于0,即后 n-m
个元素的众数也是 x
但是我们事先并不知道众数是那个,我们假设数组首个元素 n1
为众数,遍历并统计票数。当发生票数之和为0时,剩余数组的众数一定不变 ,这是由于:
- 如果
n1
是众数,抵消的数字中有一半是众数 - 如果
n1
不是众数,那么抵消的数字中n1
占一半,其他数(有可能包括真正众数)占一半,因此,抵消的数字中少于或等于一半是众数
利用此特性,每轮假设发生票数和为0都可以缩小剩余数组区间。当遍历完成时,最后一轮假设的数字即为众数。
算法流程:
- 初始化:票数统计
votes = 0
,众数 x ; - 循环:遍历数组 nums 中的每个数字 num
- 当票数 votes 等于0,则假设当前数字 num 是众数
- 当
num = x
时,票数 votes 自增 1 ;当num != x
时,票数 votes 自减 1 ;
- 返回值:返回 x 即可
class Solution {
public:
int majorityElement(vector<int>& nums) {
int x = 0, votes = 0;
for(int num : nums){
if(votes == 0) x = num;
votes += num == x ? 1 : -1;
}
return x;
}
};