一.题目

二.思路讲解
2.1 思路讲解
本题要求找出频率最高的 k 个单词,若频率相同则按字典序升序输出。
-
首先,使用哈希表统计每个单词的出现次数。
-
然后,我们需要从这些单词中选出频率最高的 k 个。这里采用小根堆来维护当前频率最高的 k 个单词。
-
堆中元素为
pair<string, int>,分别表示单词及其出现次数。 -
自定义比较规则:频率低的优先级高 (放在堆顶),频率相同时字典序大的优先级高 (即字典序大的更靠近堆顶)。这样堆顶始终是频率最小 且同频率字典序最大的单词,也就是当前候选中最"差"的一个。
-
遍历哈希表,将每个单词加入堆。如果堆大小超过 k,则弹出堆顶(即淘汰当前最差的候选)。
-
遍历结束后,堆中剩下的就是频率最高的 k 个单词,但顺序是频率升序、同频率字典序降序。
-
-
最后,将堆中的单词依次弹出,并从结果数组的末尾开始填充 (即逆序存放),这样最终数组就是按频率降序、同频率字典序升序的顺序,符合题目要求。
三.代码演示
cpp
class Solution
{
typedef pair<string,int> P;
//仿函数
struct cmp
{
bool operator()(const P& a,const P& b)
{
if(a.second == b.second)//频率相同,那么大的在下面
{
return a.first < b.first;
}
return a.second > b.second;
}
};
public:
vector<string> topKFrequent(vector<string>& words, int k)
{
//1.统计一下每一个单词出现的个数
unordered_map<string,int> hash;
for(const auto& s:words)
hash[s]++;
//2.创建一个大小为K的堆
priority_queue<P,vector<P>,cmp>heap;
//3.TopK的逻辑
for(const auto& ch : hash)
{
heap.push(ch);
if(heap.size() > k)
heap.pop();
}
//4提取结果
vector<string> ret(k);//创建K个数组
for(int i = k - 1;i >= 0;i--)
{
ret[i] = heap.top().first;
heap.pop();
}
return ret;
}
};
四.代码讲解
一、统计词频
首先,我们需要统计每个单词出现的次数。使用 unordered_map<string, int> 哈希表,遍历整个单词列表,将每个单词作为键,出现次数作为值进行累加。这一步完成后,哈希表中记录了所有单词及其对应的频率。
二、自定义比较仿函数
为了使用小根堆 来维护频率最高的 k 个单词,我们需要自定义堆中元素的比较规则。堆中存储的是 pair<string, int>,分别代表单词和频率。 比较规则:
-
频率低的元素应被视为"更小"(即优先级更高),这样堆顶就是当前 k 个候选中最小的频率。
-
如果频率相同,则字典序大的元素应被视为"更小"(即优先级更高),这样堆顶就是同频率中字典序最大的单词。 这样,堆顶始终是当前候选中最"差"的一个(频率最小,同频率下字母顺序最靠后),方便我们在堆大小超过 k 时将其弹出。
在代码中,通过仿函数 cmp 重载 operator() 实现这一规则:
-
当两个单词频率不同时,我们希望频率小的单词优先级更低 (更容易被弹出)。因此比较函数返回
a.second > b.second,这意味着如果a的频率大于b,则a的优先级更高(即a更靠近堆底);反之,若a的频率小于b,则a的优先级更低,会排在b后面(更靠近堆顶)。 -
当两个单词频率相同时,我们希望字典序大的单词优先级更低 (更容易被弹出)。因此比较函数返回
a.first < b.first,这意味着如果a的字典序小于b,则a的优先级更高(即a更靠近堆底);反之,若a的字典序大于b,则a的优先级更低,会排在b后面(更靠近堆顶)。
三、小根堆维护 TopK
创建一个小根堆 priority_queue<P, vector<P>, cmp>,其中 P 是 pair<string,int>。 遍历哈希表中的每一个单词-频率对,将其加入堆中。如果加入后堆的大小超过 k ,则弹出堆顶(即当前频率最小且同频率字典序最大的单词)。这样,堆中始终保留着当前频率最高的 k 个单词,且堆顶是这 k 个单词中频率最低的(同频率下字典序最大的)。
四、提取结果
由于堆中元素顺序是频率升序、同频率字典序降序 ,而题目要求输出频率降序、同频率字典序升序 。因此,我们需要将堆中的元素依次弹出,并从结果数组的末尾开始填充 。 创建一个大小为 k 的字符串数组 ret,使用一个循环从 i = k-1 向下遍历到 0,每次取堆顶的单词存入 ret[i],然后弹出堆顶。这样,最终 ret 中就是按频率降序、同频率字典序升序排列的结果。
五、关键细节
-
自定义比较:这是本题的核心,需确保频率和字典序的优先级方向正确,使堆能准确筛选出前 k 个高频单词。
-
堆的大小控制:每次加入后若堆大小超过 k,立即弹出堆顶,保持堆中只有 k 个元素。
-
结果顺序处理:由于堆弹出的顺序与所需输出顺序相反,采用逆序填充结果数组。
五、流程图
