169. 多数元素 ● & 面试题 17.10. 主要元素 ●
描述
数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。
若没有,返回 -1 。
请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。
示例
输入:[1,2,5,9,5,9,5,5,5]
输出:5
输入:[3,2]
输出:-1
题解
1. 哈希表
- 时间复杂度:O(n)
- 空间复杂度:O(n)
2. 排序
将数组 nums 排序,主要元素一定在数组中点。(数组中点不一定是主要元素,需要再遍历一次判断)
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
3. Boyer-Moore 摩尔投票算法
Boyer-Moore 投票算法的基本思想是:
在每一轮投票过程中,从数组中删除两个不同的元素,直到投票过程无法继续,此时数组为空或者数组中剩下的元素都相等。
- 如果数组为空,则数组中不存在主要元素;
- 如果数组中剩下的元素都相等,则数组中剩下的元素可能为主要元素。
Boyer-Moore 投票算法的步骤如下:
-
维护一个候选主要元素 c a n d i d a t e candidate candidate 和候选主要元素的出现次数 c n t cnt cnt,初始时 c a n d i d a t e candidate candidate 为任意值, c n t = 0 cnt=0 cnt=0;
-
遍历数组 n u m s nums nums 中的所有元素,遍历到元素 num 时,进行如下操作:
- 如果
cnt == 0
,则将 num 的值赋给 candidate,否则不更新 candidate 的值; - 如果
num == candidate
,则将 cnt 加 1,否则将 cnt 减 1。
- 如果
-
遍历结束之后,得到一个可能为主要元素的值 candidate,我们无法确定最后剩下的 candidate 是经过多次抵消后剩余的主要元素,还是只是不存在主要元素的数组中的无效随机元素。
-
第二次遍历, 统计 candidate 的出现次数并进行比较。
为什么当数组中存在主要元素时,Boyer-Moore 投票算法可以确保得到主要元素?
在 Boyer-Moore 投票算法中,遇到相同的数则将 ount 加 1,遇到不同的数则将 count 减 1。根据主要元素的定义,主要元素的出现次数大于其他元素的出现次数之和,因此在遍历过程中,主要元素和其他元素两两抵消,最后一定剩下至少一个主要元素,此时 candidate 为主要元素,且 count ≥ 1
。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
class Solution {
public:
int majorityElement(vector<int>& nums) {
int candidate = nums[0], cnt = 0;
for(int num : nums){
if(cnt == 0){
candidate = num; // 更新candidate
}
if(candidate == num){ // 更新cnt
++cnt;
}else{
--cnt;
}
}
cnt = 0;
for(int num : nums){
if(num == candidate) ++cnt; // 统计 candidate 次数
}
if(2 * cnt > nums.size()){ // 判断次数
return candidate;
}
return -1;
}
};