题目:Top K Frequent Elements
原题链接:https://leetcode.com/problems/top-k-frequent-elements/
Given a non-empty array of integers, return the k most frequent elements.
For example,
Given [1,1,1,2,2,3] and k = 2, return [1,2].
Note:
You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Your algorithm’s time complexity must be better than O(n log n), where n is the array’s size.
给出一个数组,找出出现频度在前K个的元素(注意,返回的结果中对元素的顺序没有要求),题目保证K合法,并且要求时间复杂度优于O(n log n)。
首先肯定要统计不同的元素出现的次数,这里可以考虑使用无序map来映射元素以及出现的频度,之后可以考虑使用一个优先队列(频度小优先)来存储频度最高的K个元素,这样做的目的是找出第K的频度的数值,然后重新遍历无序map,把频度不小于这个数值的元素输出。
代码如下:
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> mp;
priority_queue<int, vector<int>, greater<int>> pq;
for(auto num : nums) ++mp[num];
auto i = mp.begin();
for(; i != mp.end(); ++i) {
pq.push(i->second);
if(pq.size() > k) pq.pop();//适中保持队列中只有k个频度,这样队首对应的就是当前k个中最小的频度
}
vector<int> ans;
for(i = mp.begin(); i != mp.end(); ++i) {
if(i->second >= pq.top()) ans.push_back(i->first);
}
return ans;
}
};
网上还有另外一种做法,利用C++的nth_element函数来进行排序,它可以快速的把容器中的元素进行划分,分界点就是中间的第k个元素,左边的都比第k个小,右边的都比第k个大。这里有一个小技巧,因为我们需要的是频度高的前k个,所以在存入频度时,我们可以取负号,这样排序出来前k个就是频度最高的k个元素了,代码如下:
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> mp;
for(auto num : nums) ++mp[num];
vector<pair<int, int>> ve;
auto i = mp.begin();
for(; i != mp.end(); ++i) {
ve.emplace_back(-i->second, i->first);//这里取的是负号
}
nth_element(ve.begin(), ve.begin() + k -1, ve.end());
vector<int> ans;
for(int j = 0; j < k; ++j) {
ans.push_back(ve[j].second);
}
return ans;
}
};