1、leetcode 169
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
1.1其他方法
思路:对于这个题,常见的方法时使用hashmap对数据每个元素进行统计,然后计算除每个元素的个数,最后进行提取就ok,这个方法需要二外的空间。
还有一种方法是直接对其进行排序,(快排或者冒泡),排完序之后直接返回v[v.size()/2],这个中间元素就一定是要求的元素。
代码:这里我们用快排,将其转换为求topk的问题。(求第v.size()/2+1大的元素)
class Solution {
public:
void quick_sort(vector<int>& nums, int left, int right, int k)
{
//从大到小排列
if (left >= right)
return;
int i = left, j = right;
while (i <j)
{
//从right向左找出大于nums[left]
while (i < j&&nums[j] <= nums[left])
{
j--;
}
//从left向右找出小于nums[left]
while (i < j&&nums[i] >= nums[left])
{
i++;
}
if (i < j)
swap(nums[i], nums[j]);
}
swap(nums[left], nums[i]);
int num = i - left + 1;
if (num == k)
return;
if (num > k)
quick_sort(nums, left, i - 1, k);
else
quick_sort(nums, i + 1, right, k - num);
}
int majorityElement(vector<int>& nums) {
//复习快排,转换为求topK的问题
int n=nums.size();
if(n==1)
return nums[0];
quick_sort(nums,0,nums.size()-1,nums.size()/2+1);
return nums[nums.size()/2];
}
};
或者是调用nth_element函数,
template <class RandomAccessIterator>
void nth_element (RandomAccessIterator first, RandomAccessIterator nth,
RandomAccessIterator last);
重新排列[first,last)范围内的元素,以使位于第n个位置的元素是按真实的顺序的元素(即默认第n个元素的第n+1大的数)。其他元素没有任何特定的顺序,只是第n个元素之前的元素都不比其大,而第n个元素的元素都不小于。底层还是使用的快排和选择排序的结合。
用法:
void main()
{
vector<int> v = { 7,2,1,5,0,6,4,3 };
nth_element(v.begin(), v.begin() + 3, v.end());
cout << "第3小是:" << v[2] << endl;
}
int majorityElement(vector<int>& nums) {
if(nums.size()==0)
return -1;
// sort(nums.begin(),nums.end());
nth_element(nums.begin(),nums.begin()+nums.size()/2,nums.end());
return nums[nums.size()/2];
}
1.2 摩尔投票
思路:可以理解为同归于尽算法,有候选者cond,其num=0,当num=0时,从新选择候选者,cond=nums[i],num=1,当cond !=nums[i],num–,当cond ==nums[i],num++。其实就是不停的抵消,因为总有一个元素时大于n/2的,所以最后总能剩下一个元素时该元素,当要注意最后的num并不等于所求元素的个数。因为会被之前的元素抵消。
class Solution {
public:
int majorityElement(vector<int>& nums) {
int n=nums.size();
if(n==0)
return -1;
int count=0,cand=nums[0];
for(int i=0;i<n;i++)
{
if(count==0)
{
count++;
cand=nums[i];
continue;
}
if(cand==nums[i])
count++;
else
count--;
}
return cand;
}
};
2、leetcode 229
229. 求众数 II 给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
2.1 其他方法
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
思路:这个题的思路和上个题的思路是一样的,也是可以使用map或者快排(这里快排求出nums[n/3]和nums[]),但注意这个题不保证一定存在这个数,且最多存在2个超过 ⌊ n/3 ⌋ 次的元素,所以最后要进行判断。
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
if(nums.size()==0)
return {};
int n=nums.size();
sort(nums.begin(),nums.end());
int cond1=nums[n/3],num1=0;
int cond2=nums[2*n/3],num2=0;
int cond3=nums[nums.size()-1],num3=0;
for(auto e:nums)
{
if(e==cond1) num1++;
else if(e==cond2) num2++;
else if(e==cond3) num3++;
}
vector<int> res;
if(num1>(n/3))
res.push_back(cond1);
if(num2>(n/3))
res.push_back(cond2);
if(num3>(n/3))
res.push_back(cond3);
return res;
}
};
2.2 摩尔投票
思路:选择两个候选者,cond1和cond2,个数分别为num1,num2
则再循环判断的时候,存在四种情况:
1、果cond1==e,则候选者1的 票数++;
2、如果cond2==e,则候选者2 的 票数++;
3、如果num1==0,cond1候选者更新为e;
4、如果num2==0,cond2候选者更新为e;
最后会有这么几种可能:有2个大于n/3,有1个大于n/3,有0个大于n/3,因为遍历结束后一定会选出了两个候选人,但是这两个候选人是否满足>n/3,还需要再遍历一遍数组,找出两个候选人的具体票数,因为题目没有像169题保证一定有。
vector<int> majorityElement(vector<int>& nums) {
int n = nums.size();
vector<int> res;
if (n == 0)
return {};
// 定义两个候选者和它们的票数
int cond1 = 0, cond2 = 0;
int num1 = 0, num2 = 0;
for (auto e : nums)
{
//如果cond1==e,则候选者1 票数++
if (cond1 == e)
{
num1++;
continue;
}
//如果cond2==e,则候选者2 票数++
if (cond2 == e)
{
num2++;
continue;
}
//如果num1=0,cond1候选者更新为e
if (num1 == 0)
{
cond1 = e;
num1++;
continue;
}
//如果num1=0,cond2候选者更新为e
if (num2 == 0)
{
cond2 = e;
num2++;
continue;
}
num1--;
num2--;
}
num1 = num2 = 0;
for (auto e : nums)
{
if (cond1 == e)
num1++;
else if (cond2 == e)
num2++;
}
if (num1 > (n / 3))
res.push_back(cond1);
if (num2 > (n / 3))
res.push_back(cond2);
return res;
}