思路:
题目的主要信息:
- 需要统计字符串出现的频率
- 最大复杂度为O(nlgk)
- 选出频率前K,相同频率算字典序最小
方法一:排序法
使用先统计次数,再排序,再找出前k的策略,但是不符合题目要求!!!!
不符合规定
复杂度分析:
- 时间复杂度:O(nlgn),排序算法最快O(nlgn),遍历前k个
- 空间复杂度:O(1),无额外空间
方法二:哈希表+小顶堆
拥有关联的一组数据快速访问,我们想到了哈希表,快速找到topK我们想到了小顶堆:当超过k时,与堆顶比较,若堆顶较小,则将堆顶pop出,加入新的元素。
具体做法:
使用map当哈希表,记录字符串与出现次数,做到快速访问。遍历哈希表,将其加入小顶堆中,如果超过了k则用上述策略更新堆。(当然堆的比较策略需要重载:出现频率更高的或者相同时字典序更小的) 最后输出因为是小顶堆,所以需要逆序输出。
class Solution {
public:
struct cmp { //重载比较规则
bool operator() (pair<string, int> p1, pair<string, int> p2) {
//返回出现频率更高的或者相同时字典序更小的
return p1.second > p2.second || (p1.second == p2.second && p1.first < p2.first);
}
};
vector<vector<string> > topKstrings(vector<string>& strings, int k) {
vector<vector<string> > res;
unordered_map<string, int> mp; //统计字符串出现次数
priority_queue<pair<string, int>, vector<pair<string, int> >, cmp> pq; //重载比较的小顶堆
for(int i = 0; i < strings.size(); i++) //次数加入哈希表
mp[strings[i]]++;
for(auto iter = mp.begin(); iter != mp.end(); iter++) {
if(pq.size() < k) //用k限制堆大小
pq.push(pair<string, int> {iter->first, iter->second});
//当超过k,需要判断是否将最小的取出加入新的
else if(iter->second > pq.top().second || (iter->second == pq.top().second && iter->first < pq.top().first)) {
pq.pop();
pq.push(pair<string, int> {iter->first, iter->second});
}
}
while(!pq.empty()) {
res.push_back(vector<string> {pq.top().first, to_string(pq.top().second)});
pq.pop();
}
reverse(res.begin(), res.end()); //逆转使优先级从大到小
return res;
}
};
复杂度分析:
- 时间复杂度:O(nlgk),维护堆每次为lgk,最多为n次
- 空间复杂度:O(n),哈希表最大为n