众所周知,map结构可以基于key值默认从小到大的排序,但是unordered_map对数据的处理方式比map更快和更便捷。
做题时,基于key我们可以将unordered_map换成map,但是有时候我们不得不于value值的排序
比如下面这个题目:
首先第一想法肯定是使用哈希表存储每一个元素出现的次数,为了拿到前k个优先元素,排序成了一个大问题
避坑经验:
unordered_map不能使用sort进行自定义排序,这是在官方文档里面有说明的;
所以要使用堆、也就是优先队列
堆分为大顶堆和小顶堆,大顶堆就是将队列里面最大的元素放在队首,而小顶堆就是将队列里面最小的元素放在队首。堆的声明默认是一个大顶堆,我们可以很轻易的拿到队列当作最大的元素。
至于队列的顺序,堆只是将最值放在队首,并不会对队列严格排序;
声明一个大顶堆:需要引用头文件:#include<queue>
priority_queue<int> p;
对于基本数据类型(int, char等),声明小顶堆有自带的比较函数
priority_queue<int, vector<int>, greater<int> > q;
对于priority_queue的参数列表:第一个是数据类型,第二个是存储类型的container容器,一般是vector<typename>,第三个则是比较函数,大顶堆的话后面两个参数可以省略。
greater<int>就是小顶堆的自带比较函数
但是对于像pair<int,int>(队组)这样的数据类型,priority_queue就没有自带比较函数了,不但需要自己写比较函数,声明上也有些许不同:
//比较函数
static bool cmp(pair<int,int>& a, pair<int,int>b){
return a.second > b.second;
}
//声明
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> q(cmp);
其中的decltype函数是自动类型推断,可以将pair<int,int>等比较复杂的数据类型识别出来,但是要传入比较函数的地址
我们利用小顶堆不断的取出unordered_map里面的小元素,这样算是一层筛选,对于中等题,基本就可以过了。
接下来请看本题代码吧:
class Solution {
public:
static bool cmp(pair<int,int>& a, pair<int,int>b){
return a.second > b.second;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int>ump;
int n = nums.size();
vector<int>ans;
for(int i = 0; i < nums.size(); i++){
ump[nums[i]]++;
}
//创建一个pair类型的小顶堆
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> q(cmp);
//decltype用于自动类型推断、将pair依照value值把最小的放在前面
// a是nums[i]数值,b是此数值出现的次数
for(auto& [a,b] : ump){
if(q.size() == k){
if(q.top().second < b){
q.pop();
q.emplace(a,b);
}
}
else q.emplace(a,b);
}
while(q.size() != 0){
ans.push_back(q.top().first);
q.pop();
}
return ans;
}
};
希望和诸君共勉!