多数元素
题目
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
假设数组是非空的,并且给定的数组总是存在多数元素。
示例
输入:[3,2,3]
输出:3
题解
方法 1 哈希表
利用哈希映射来存储每个元素及其出现的次数,键表示一个元素,值表示该元素出现的次数。为了找出其中的多数元素,可以在哈希表构造完成之后遍历,返回值最大的键,或者在哈希表构造过程中采用打擂台的方法,维护最大的值,省去最后的遍历步骤。
方法 2 排序
由于数组一定存在出现次数大于一半的多数元素,所以若将数组元素进行排序,无论单调递增还是单调递减,第 ⌊ n/2 ⌋ +1 个元素(数组中下标为 ⌊ n/2 ⌋ ) 一定是该多数元素。
方法 3 随机化
由于多数元素占据超过一半,因此随机挑选一个元素并验证,很大概率能找到该多数元素。因此,可以随机选择数组下标,检查该下标对应的元素是否为众数,如果是则返回,否则继续随机选择下标。
方法 4 分治
假设 a 是数组 nums 的众数,将 nums 分成两部分,则 a 至少是其中一部分的众数。因此,可以采用分治法,将数组分成左右两部分,分别求出左半部分和右半部分的众数,从中选出正确的结果。
采用分治法分解大问题为多个子问题,直至所有子问题都是长度为 1 的数组。若数组长度为 1,则直接返回;若数组长度大于 1,则划分左右子区间,比较左右区间的众数,如果众数相同,则返回,如果不同则计算两个众数在整个区间的出现次数,大于一半者为该区间的众数。
方法 5 Boyer-Moore 投票算法
-
算法步骤:
a. 第一趟扫描得到一个候选节点 candidate
b. 第二趟扫描判断 candidate 出现次数是否大于 ⌊ n/2 ⌋在第一趟扫描结束后,如果数组中存在多数元素,则 candidate 即为多数元素,如果数组中不存在多数元素,则 candidate 无意义。第二趟扫描是用来统计 candidate 出现的次数从而判断其是否为多数元素。
注:在本题中给定的数组一定存在多数元素,故只需要第一趟扫描。 -
相关变量:
a. candidate:初值可以为任何数
b. count:初值为0对于数组中每一个元素,先判断 count 是否为0,若为 0 则将 candidate 设置为当前元素,再判断 candidate 是否与当前元素相等,若相等则 count++,否则 count–。
-
算法思想:
假设数组第一个元素 m 不是多数元素,则一开始扫描到第一个元素时 candidate 设置为当前元素 m,count++ 为1,然后继续向后扫描,当遇到数组元素为 m 时 count++,否则 count–。由于 candidate 为 m(不是多数元素),所以 count 一定会减到 0,然后下一个扫描的元素将重新赋值给 candidate。
在 count 值减为 0 的过程中,与 candidate 相抵消的元素:若全为多数元素,则 count 变为 0 的前一部分中多数元素 n 占一半,因为多数元素占数组的一半以上,故 n 仍为剩下部分的多数元素,该前一部分可以直接忽略。若不全为多数元素,则剩下部分中存在更多的多数元素, n 仍为剩下部分的多数元素。
假设数组第一个元素 m 是多数元素,则当 count 减为 0 时消耗了与多数元素一样多的非多数元素,m 仍为数组剩余部分的多数元素。
上述两种情况说明:数组中从 candidate 被赋值到 count 减为 0 的那一段可以去除,余下部分的多数元素仍是原数组的多数元素。
扫描一遍数组完成后 count 一定大于 0 ,此时 candidate 即为原数组的多数元素。
代码
方法 1 哈希表
- 哈希表存储(key 为元素,value 为元素的出现次数)
- 打擂台方法维护 value 最大值对应的元素
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int,int> counts;
int majority=0,cnt=0;
for(int num:nums){
++counts[num];
if(counts[num]>cnt){
cnt=counts[num];
majority=num;
}
}
return majority;
}
};
方法 2 排序
- 排序:升序 sort(begin,end) ;降序 sort(begin,end,compare)
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(),nums.end());
return nums[nums.size()/2];
}
};
方法 3 随机化
- rand() 函数:返回一个从0到最大随机数的任意整数
int num = rand() % n +a;
a是起始值,n-1+a是终止值,n是整数的范围
class Solution {
public:
int majorityElement(vector<int>& nums) {
while(true){
int candidate=nums[rand()%nums.size()];
int count=0;
for(int num:nums){
if(num==candidate){
++count;
}
}
if(count>nums.size()/2){
return candidate;
}
}
return -1;
}
};
方法 4 分治
- count_in_range(vector& nums,int target,int lo,int hi)
计算区间 nums(lo,hi) 内 target 出现的次数 - majority_element_rec(vector& nums,int lo,int hi)
求出区间 nums(lo,hi) 内的众数(多数元素) - majorityElement(vector& nums)
求出数组 nums 中的多数元素
class Solution {
int count_in_range(vector<int>& nums,int target,int lo,int hi){
int count=0;
for(int i=lo;i<=hi;i++){
if(nums[i]==target){
++count;
}
}
return count;
}
int majority_element_rec(vector<int>& nums,int lo,int hi){
if(lo==hi){
return nums[lo];
}
int mid=(lo+hi)/2;
int left_majority=majority_element_rec(nums,lo,mid);
int right_majority=majority_element_rec(nums,mid+1,hi);
if(count_in_range(nums,left_majority,lo,hi)>(hi-lo+1)/2){
return left_majority;
}
if(count_in_range(nums,right_majority,lo,hi)>(hi-lo+1)/2){
return right_majority;
}
return -1;
}
public:
int majorityElement(vector<int>& nums) {
return majority_element_rec(nums,0,nums.size()-1);
}
};
方法 5 Boyer-Moore 投票算法
class Solution {
public:
int majorityElement(vector<int>& nums) {
int candidate=0,count=0;
for(int num:nums){
if(count==0){
candidate=num;
}
if(candidate==num){
count++;
}else{
count--;
}
}
return candidate;
}
};