题目:
Given an integer array of size n,
find all elements that appear more than ⌊ n/3 ⌋
times.
The algorithm should run in linear time and in O(1) space.
思路:
1、Partition方法:可以调用基本快速排序中的partition方法,该方法可以在线性时间内,按照某个数将数组分为两部分,其中左边是比这个数小的数,右边是比这个数大的数。在这个题目中,我们要找到出现次数超过1/3的数,那么这样的数最多有两个,利用partition找出应该出现在数组1/3以及2/3处的值,则出现超过1/3的数必然在这两个数中。然后再验证这两个数是不是解。这种解法期望的时间复杂度是O(nlogn),但是在最坏情况下时间复杂度依然是O(N^2)。Partition方法可以通过Leetcode上面的测试用例,但是耗时非常多。
2、BM(Boyer-Moore Majority Vote Algorithm)投票法:设置一个计数器,在遍历数组的时候,如果是这个数,则计数器加1,否则减1,该方法用来计数超过一半的数非常方便。在这里我们改进一下BM投票计数法:设置两个计数器,如果是两个数中的一个,则对应的计数器加1,如果不是这两个数中的任何一个,则两个计数器都减1。如果计数器为0了,则统计当前这个数。那么如果一个数出现次数超过1/3,则最后必然出现在统计的数中,但是我们不确定得到的这两个数出现次数是否超过1/3,因此最后需要验证一下。该算法的时间复杂度是O(n),空间复杂度是O(1)。
代码:
1、Partition方法:
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
if (nums.size() == 0) {
return {};
}
int length = nums.size(), left = 0, right = length - 1;
int num1 = 0, num2 = 0, pos = 0;
while ((pos = partition(nums, left, right)) != length / 3) {
pos < length / 3 ? left = pos + 1 : right = pos - 1;
}
num1 = nums[length / 3];
left = 0, right = length - 1;
while((pos = partition(nums, left, right)) != length * 2 / 3) {
pos < length * 2 / 3 ? left = pos + 1 : right = pos - 1;
}
num2 = nums[length * 2 / 3];
int count1 = 0, count2 = 0;
for (auto val : nums) {
if (val == num1) {
++count1;
}
else if (val == num2) {
++count2;
}
}
vector<int> ret;
if (count1 > length / 3) {
ret.push_back(num1);
}
if (count2 > length / 3) {
ret.push_back(num2);
}
return ret;
}
private:
int partition(vector<int>& nums, int left, int right) {
int val = nums[right];
for(int i = left; i < right; ++i) {
if(nums[i] < val) {
swap(nums[left++], nums[i]);
}
}
swap(nums[right], nums[left]);
return left;
}
};
2、BM投票法:
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
if (nums.size() == 0) {
return {};
}
int num1, num2, count1 = 0, count2 = 0;
for (auto val : nums) {
if (val == num1) {
++count1;
}
else if (val == num2) {
++count2;
}
else if (count1 == 0) {
++count1, num1 = val;
}
else if (count2 == 0) {
++count2, num2 = val;
}
else {
--count1, --count2;
}
}
count1 = 0, count2 = 0;
for (auto val : nums) {
if (val == num1) {
++count1;
}
else if (val == num2) {
++count2;
}
}
vector<int> ret;
if (count1 > nums.size() / 3.0) {
ret.push_back(num1);
}
if (count2 > nums.size() / 3.0) {
ret.push_back(num2);
}
return ret;
}
};