题目描述:
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋
次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
示例 1:
输入: [3,2,3]
输出: [3]
示例 2:
输入: [1,1,1,3,3,2,2,2]
输出: [1,2]
解题思路:
摩尔投票法:
摩尔投票算法的时间和空间都很低,时间复杂度为O(n),空间复杂度为O(1),可以快速的计算出一个数组中出现次数过半的数即大多数(majority),算法核心思想是同加,异减
。算法会保存一个当前大多数,和得分,当遇到一个数不是当前大多数时,得分会减一,当减到0时,大多数会发生改变,并且重置得分为1。
这里需要区分的是,摩尔算法不能用来得到众数(mode),例如数组:[1,1,1,2,2,3,3,4,4],摩尔算法得出最后的结果应该是4,但4并不是众数,可是显然4也不是大多数,那是因为,大多数是指出现次数过半的数,而这个数组中没有这样的数,所以摩尔算法是失效的,对于这种情况采取需要重新投票。
算法伪代码:
初始化元素 major = 0, count = 0
遍历数组中每一个元素x:
if count == 0
major = x
count = 1
else if major == x
++count
else
--count
return major
leetcode 229 解题:
每次删除三个不相同的数,最后留下的一定是出现次数超过1/3的数,这个思想可以推广到出现次数超过1/k次的元素有哪些。
- 因为出现次数大于n/3的元素最多只有两个,所以最开始可以维护两个数字(num1,num2)和两个计数器(counter1,counter2);
- 遍历数组,当数组中元素和num1或者num2相同,对应的counter1或者counter2加1;
- 如果counter1或counter2为0,将遍历到的该元素赋给num1或者nums2;
- 否则counter1和counter2都减1。
代码:
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
vector<int> res;
int value1 = INT_MIN, value2 = INT_MIN, count1 = 0, count2 = 0;
for(auto i : nums){
if(i == value1)
++count1;
else if(i == value2)
++count2;
else if(count1 == 0){
value1 = i;
count1 = 1;
}
else if(count2 == 0){
value2 = i;
count2 = 1;
}
else{
--count1;
--count2;
}
}
int counter1 = 0, counter2 = 0;
for(auto i : nums){ //第二遍遍历来确定value1与value2是否大于 n/3
if(i == value1)
++counter1;
else if(i == value2)
++counter2;
}
if(counter1 > nums.size() / 3)
res.push_back(value1);
if(counter2 > nums.size() / 3)
res.push_back(value2);
return res;
}
};