相信这类帖子在网上已经很多了,本帖很基础,仅对两个问题进行总结与收录:
- 面试常考题:海量数据TOP-K
- 笔试题:词频或者数字等TOP-K
须知,面试与笔试还是迥然不同的,面试侧重于基础知识是否扎实与知识体系是否完善,笔试则纯粹是要快刀斩乱麻,能得分就行。
1.面试题:海量数据TOP-K
参考
1)直接排序(不推荐,工程中不现实):如果内存足够,并且数据范围已知,可以使用计数排序(桶排序),否则通常不适用。
2)分治:分成若干份,分别局部排序(可使用快排,注意内存限制),然后进行归并。
3)局部淘汰:最小堆(或者优先队列)
2.笔试题:TOP-K
以下摘自
leetcode
中若干题目,我的解法。主要调用标准库函数
。
2.1 215. 数组中的第K个最大元素 middle
- 傻瓜方法:直接调用sort
- 基于快排分区函数
- 基于堆调整的局部淘汰
解法3:基于堆调整的局部淘汰
基本技能:堆调整max_heapify函数
代码如下:
2.2 前K个高频元素 middle
解法1:正则表达式排序(略)
解法2:优先队列
直接给出代码:
// 高频面试题:前K个高频元素
// Created by wbzhang on 2020/9/29.
// leetcode No.347
#include <vector>
#include <unordered_map>
#include <map>
#include <queue>
using namespace std;
typedef pair<int,int> pint;
bool cmp(const pint& pa,const pint& pb)
{
return pa.second > pb.second;
}
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
// 哈希表统计频率
unordered_map<int,int> ump;
for(auto ele:nums) ump[ele]++; //数字,频率
// 使用优先队列
priority_queue< pint,vector<pint>, decltype(&cmp) > pq(cmp); // 大顶堆,但是重载比较函数中的频率排序
for(auto ele:ump){
if(pq.size()<k ){
pq.push(ele);
}else{
// 按频率局部淘汰
if(ele.second > pq.top().second){
pq.pop();
pq.push(ele);
}
}
}
vector<int> res;
while(!pq.empty()){
res.push_back(pq.top().first);
pq.pop();
}
return res;
}
// vector<pint> hist(ump.begin(),ump.end() );
};
2.2 前K个高频单词 middle
leetcode No.692
解法1:库函数sort
排序
笔试推荐写法
,其中基础技巧包括正则表达式
。
代码如下:
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
unordered_map<string,int> ump;
for(auto str:words) ump[str]++;
vector<pair<string,int> > vecstri(ump.begin(),ump.end() );
sort(vecstri.begin(),vecstri.end(),[](const pair<string,int> &pa,const pair<string,int> &pb)
{
return pa.second > pb.second ? true : (pa.second ==pb.second && pa.first<pb.first);
});
vector<string> res;
for(int i=0;i<k;++i){
res.push_back(vecstri[i].first);
}
return res;
}
};
解法2:使用优先队列priority_queue
局部淘汰
同样调用库函数,使用优先队列(相当于最小堆),基础技巧:使用自定义优先队列时需要重载比较函数
。此处自定义优先队列的比较函数注意要使用结构体内部重载operator()
的形式,使用正则表达式或者直接定义比较函数
均不够好用。
// 自定义比较函数(不推荐)
bool cmp(const pair<string,int> &pa,const pair<string,int> &pb)
{
return pa.second>pb.second ? true : (pa.second == pb.second && pa.first < pb.first);
}
// 对应的pq声明写法如下,故不建议使用
priority_queue<strint,vector<strint>, decltype(&cmp) > pq(cmp);
代码如下:
#include<queue>
typedef pair<string,int> strint;
struct cmp{
bool operator()(const strint &pa,const strint &pb){
return pa.second>pb.second ? true : (pa.second == pb.second && pa.first < pb.first);
}
};
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
unordered_map<string,int> ump;
for(auto str:words) ump[str]++;
// 优先队列
priority_queue<strint,vector<strint>, cmp> pq;
for(auto ele:ump){
if(pq.size() < k){
pq.push(ele);
}else{
if(ele.second>pq.top().second
|| (ele.second==pq.top().second && ele.first <pq.top().first ) ){
pq.pop();
pq.push(ele);
}
}
}
vector<string> res;
while(!pq.empty() ){
res.push_back(pq.top().first );
pq.pop();
}
reverse(res.begin(),res.end());
return res;
}
};