TopK问题,就是找到一堆数据中最大/最小、出现频率最高等问题。现在想一下其实解决TopK问题的话,有一下几个想法:
1、直接各种排序,前提是内存足够的话,如果数据量极大,那就不太抗得住了。
2、最大最小堆。保留K个值,然后其他值与之比较,对最大/最小堆进行增加删除操作。
3、分治思想。把所有数据分成无数的k段,然后再合并不同的段称为新的k段,直到最后剩下一个段,即为所求。
4、hash法
下面就用实例来上手一下:
Leetcode347. 前 K 个高频元素
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
class Solution {
public:
//此时要定义为静态函数,否则在类中无法直接调用
static bool cmp(pair<int,int> m1,pair<int,int> m2)
{
return m1.second>m2.second;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
//用map来统计每个元素的个数
map<int,int> m;
for(int i=0;i<nums.size();i++)
{
m[nums[i]]++;
}
//将每个map放入vector中(用迭代器),便于后续的按数量多少进行排序
vector<pair<int,int>> v;
map<int,int>::iterator it;
for(it=m.begin();it!=m.end();it++)
{
v.push_back(*it);
}
//依据自定义的静态函数,进行排序,数量多的在前面
sort(v.begin(),v.end(),cmp);
//将数量topk元素放入结果向量中
vector<int> res;
for(int i=0;i<k;i++)
{
res.push_back(v[i].first);
}
return res;
}
};
Leetcode692. 前K个高频单词
给一非空的单词列表,返回前 k 个出现次数最多的单词。
**注意:**返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。
输入: [“i”, “love”, “leetcode”, “i”, “love”, “coding”], k = 2
输出: [“i”, “love”]
解析: “i” 和 “love” 为出现次数最多的两个单词,均为2次。
注意,按字母顺序 “i” 在 “love” 之前。
class Solution {
public:
static bool cmp(pair<string,int> m1,pair<string,int> m2)
{
if(m1.second==m2.second) return m1.first<m2.first;
return m1.second>m2.second;
}
vector<string> topKFrequent(vector<string>& words, int k) {
map<string,int> m;
for(int i=0;i<words.size();i++)
{
m[words[i]]++;
}
vector<pair<string,int>> v;
map<string,int>::iterator it;
for(it=m.begin();it!=m.end();it++)
{
v.push_back(*it);
}
sort(v.begin(),v.end(),cmp);
vector<string> v1;
for(int i=0;i<k;i++)
{
v1.push_back(v[i].first);
}
return v1;
}
};
可以看出,这和第一题的答案几乎相同,只是自定义的比较函数考虑的数量相同情况下的顺序问题。所以这里也不需要再注释了。
下面利用最大最小堆来解决TopK问题:
构造大顶堆:
//构造一个空的优先队列(此优先队列默认为大顶堆)
priority_queue<int> big_heap;
//另一种构建大顶堆的方法
priority_queue<int,vector<int>,less<int> > big_heap2;
构造小顶堆:
//构造一个空的优先队列,此优先队列是一个小顶堆
priority_queue<int,vector<int>,greater<int> > small_heap;
需要注意的是,如果使用less和greater,需要头文件:
#include
剑指 Offer 40. 最小的k个数
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> v(k,0);
if (k == 0) return v; // 排除 0 的情况
//priority_queue q;构造大顶堆的两种方式
priority_queue<int,vector<int>,less<int>> q;
for(int i=0;i<k;i++)
{
q.push(arr[i]);
}
for(int i=k;i<arr.size();i++)
{
if(arr[i]<q.top())
{
q.pop();
q.push(arr[i]);
}
}
for (int i = 0; i < k; ++i) {
v[i] = q.top();
q.pop();
}
return v;
}
};