1,题目要求
Given a non-empty array of integers, return the k most frequent elements.
Example 1:
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]
Example 2:
Input: nums = [1], k = 1
Output: [1]
Note:
- You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
- Your algorithm’s time complexity must be better than O(nlogn), where n is the array’s size.
给定非空的整数数组,返回k个最常见的元素。
2,题目思路
对于这道题,要求求一个数组中出现频率前k个元素。因此,这道题首先就需要对每个元素出现的次数进行统计和计算。
在统计元素的个数上,使用容器map是一个非常快捷的办法。
之后,我们需要找到出现次数在前k个的数字。
如果我们可以直接对map的second元素进行排序,结果将会非常的简单,但是map不可以使用sort方法,因此,我们必须人为地去找到前k频繁的元素。
在如何实现这个功能上,我们这里有两种方法:
第一种方法,是利用C++内置的一种容器:
priority_queue,优先队列
对于这个容器,之后会单独写一篇博客做学习记录。
这里只需要知道,它是一种堆结构,在构建的过程中,会按照一定的顺序来构建,有点类似于map的构建过程(map是基于红黑树)。
因此,在构建这个优先队列时,根据map中的second元素,默认会构建一个大顶堆。
构建完成之后,取前k个元素的first的值,就可以得到对应的前k个最频繁的元素。
第二种方法,则是使用桶排序的方法。
这种方法也比较直观。
同样的,在使用map实现记录每个元素出现的次数之后,构建一个二维向量,利用桶的思路,将出现的次数作为索引,每个索引对应出现这个次数的数字。
从后往期按顺序将这些数字加入到返回数组中,当返回数组的长度达到k时,就说明找到了前k最频繁的元素,返回即可。
3,代码实现
1,priority_queue
int x = []() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
return 0;
}();
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
map<int, int> frequentMap;
for(auto &n : nums)
frequentMap[n]++; //统计数组中每个数字出现的次数
//利用优先队列中的特点来取前k
//默认构建大顶堆
priority_queue<pair<int, int>> q;
for(auto &fm : frequentMap)
q.push(make_pair(fm.second, fm.first));
vector<int> res;
while(k--){
res.push_back(q.top().second);
q.pop();
}
return res;
}
};
2,桶排序
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
int maxVal = INT_MIN;
unordered_map<int, int> frequentMap;
for(auto &n : nums){
frequentMap[n]++;
maxVal = max(maxVal, frequentMap[n]);
}
vector<vector<int>> backet (maxVal+1);
for(auto &fm : frequentMap)
backet[fm.second].push_back(fm.first);
vector<int> res;
for(int i = maxVal;i>=0 && res.size()<k;i--){
for(auto &n : backet[i]){
res.push_back(n);
if(res.size() == k)
break;
}
}
return res;
}
};