给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
输入:[2,2,1,1,1,2,2]
输出:2
输入:[3,2,3]
输出:3
先排序,然后采用守擂台的思路,当ret值满足条件时就返回
时间复杂度O(nlogn),空间复杂度O(1)
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(),nums.end());
int curNum = nums[0];
int ret = 0;
for(int i = 0; i < nums.size(); i++){
if(curNum == nums[i]){
ret++;
if(ret > nums.size()/2){
return nums[i];
}
} else{
curNum = nums[i];
ret = 1;
}
}
return -1;
}
};
*总结的其它方法:
一、哈希表法
开始最先想到的方法,思路:
数组nums每个元素对应哈希表的键值,如:
2 | 2 | 1 | 1 | 1 | 2 | 2 |
---|
那么哈希表的长度应该为3(nums.size() + 1)
0 | 1 | 2 |
---|---|---|
0次 | 3次 | 4次 |
但是这样存贮忽略了nums中存在负数的情况。
官方解答:
使用HashMap来存储每个元素以及出现的次数
对于每个键值对,键:一个元素,值:该元素出现的次数
关于unordered_map的介绍
关键点:
(1)unordered_map通过key去检索value,而不是通过绝对地址(和顺序容器不同)
(2)unordered_map主要使用的是模板的前2个参数<键,值>
unordered_map<const Key, T> map;
(3)迭代器
unordered_map<Key,T>::iterator it;
(*it).first;
// the key value (of type Key)
(*it).second;
// the mapped value (of type T)
(*it);
// the "element value" (of type pair<const Key,T>)
比如这里[3,1,3] 键 出现的次数 值
class Solution {
public:
int majorityElement(vector<int>& nums) {
//<键,对应的值>
//<nums[i],次数>
unordered_map<int, int> counts;
int key = 0;
int value = 0;
for(int num : nums){
++counts[num];
if(counts[num] > value){
key = num;
value = counts[num];
}
}
return key;
}
};
二、排序
官方排序思路妙啊!
如果将数组 nums
中的所有元素按照单调递增或单调递减的顺序排序,那么下标为n/2
的元素(下标从 0 开始)一定是众数。
数组为奇数个时:
2 | 2 | 2 | 2 | 4 | 5 | 6 |
---|
数组为偶数个时:
2 | 2 | 2 | 2 | 4 | 5 |
---|
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
};
三、分治法
将数组分成左右两部分,分别求出左半部分的众数 a1 以及右半部分的众数 a2,随后在 a1 和 a2 中选出正确的众数
分治算法递归求解,直到所有的子问题都是长度为 1 的数组。
(1)长度为 1 的子数组中唯一的数显然是众数,直接返回即可。
(2)如果回溯后某区间的长度大于 1,我们必须将左右子区间的值合并。如果它们的众数相同,那么显然这一段区间的众数是它们相同的值。否则,我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数。
分治法模板
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);
}
};
四、摩尔投票法
候选人(cand_num)初始化为nums[0],票数count初始化为1。
当遇到与cand_num相同的数,则票数count = count + 1,否则票数count = count - 1。
当票数count为0时,更换候选人,并将票数count重置为1。遍历完数组后,cand_num即为最终答案。
这就相当于每个“多数元素”和其他元素 两两相互抵消,抵消到最后肯定还剩余至少1个“多数元素”。
无论数组是1 2 1 2 1,亦或是1 2 2 1 1,总能得到正确的候选人。
看到这里:自己的解法就是**
class Solution {
public:
int majorityElement(vector<int>& nums){
int candNum = nums[0];
int count = 0;
int len = nums.size();
for(int i = 0; i < len; i++){
if(candNum == nums[i]){
count++;
} else{
count--;
if(count == 0){
candNum = nums[i];
count = 1;
}
}
}
return candNum;
}
};