[LeetCode] Top K Frequent Words 前K个高频词

Given a non-empty list of words, return the k most frequent elements.

Your answer should be sorted by frequency from highest to lowest. If two words have the same frequency, then the word with the lower alphabetical order comes first.

Example 1:

Input: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
Output: ["i", "love"]
Explanation: "i" and "love" are the two most frequent words.
    Note that "i" comes before "love" due to a lower alphabetical order.

Example 2:

Input: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
Output: ["the", "is", "sunny", "day"]
Explanation: "the", "is", "sunny" and "day" are the four most frequent words,
    with the number of occurrence being 4, 3, 2 and 1 respectively.

Note:

  1. You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
  2. Input words contain only lowercase letters.

 Follow up:

  1. Try to solve it in O(n log k) time and O(n) extra space.
  2. Can you solve it in O(n) time with only O(k) extra space? 

这道题让我们求前K个高频词,跟之前那道题Top K Frequent Elements极其类似,换了个数据类型就又是一道新题。唯一的不同就是之前那道题对于出现频率相同的数字,没有顺序要求。而这道题对于出现频率相同的单词,需要按照字母顺序来排。但是解法都一样,还是用最大堆和桶排序的方法。首先来看最大堆的方法,思路是先建立每个单词和其出现次数之间的映射,然后把单词和频率的pair放进最大堆,如果没有相同频率的单词排序要求,我们完全可以让频率当作pair的第一项,这样priority_queue默认是以pair的第一项为key进行从大到小的排序,而当第一项相等时,又会以第二项由大到小进行排序,这样就与题目要求的相同频率的单词要按字母顺序排列不相符,当然我们可以在存入结果res时对相同频率的词进行重新排序处理,也可以对priority_queue的排序机制进行自定义,这里我们采用第二种方法,我们自定义排序机制,我们让a.second > b.second,让小频率的词在第一位,然后当a.second == b.second时,我们让a.first < b.first,这是让字母顺序大的排在前面(这里博主需要强调一点的是,priority_queue的排序机制的写法和vector的sort的排序机制的写法正好顺序相反,同样的写法,用在sort里面就是频率小的在前面,不信的话可以自己试一下)。定义好最小堆后,我们首先统计单词的出现频率,然后组成pair排序最小堆之中,我们只保存k个pair,超过了就把队首的pair移除队列,最后我们把单词放入结果res中即可,参见代码如下:

解法一:

class Solution {

public:

    vector<string> topKFrequent(vector<string>& words, int k) {
        vector<string> res(k);
        unordered_map<string, int> freq;
        auto cmp = [](pair<string, int>& a, pair<string, int>& b) {
            return a.second > b.second || (a.second == b.second && a.first < b.first);
        };
        priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp) > q(cmp);
        for (auto word : words) ++freq[word];
        for (auto f : freq) {
            q.push(f);
            if (q.size() > k) q.pop();
        }
        for (int i = res.size() - 1; i >= 0; --i) {
            res[i] = q.top().first; q.pop();
        }
        return res;
    }
};

下面这种解法还是一种堆排序的思路,这里我们用map,来建立次数和出现该次数所有单词的集合set之间的映射,这里也利用了set能自动排序的特性,当然我们还是需要首先建立每个单词和其出现次数的映射,然后将其组成pair放入map种,map是从小到大排序的,这样我们从最后面取pair,就是次数最大的,每次取出一层中所有的单词,如果此时的k大于该层的单词个数,就将整层的单词加入结果res中,否则就取前K个就行了,取完要更更新K值,如果K小于等于0了,就break掉,返回结果res即可,参见代码如下: 

解法二:

class Solution {

public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        vector<string> res;
        unordered_map<string, int> freq;
        map<int, set<string>> m;
        for (string word : words) ++freq[word];
        for (auto a : freq) {
            m[a.second].insert(a.first);
        }
        for (auto it = m.rbegin(); it != m.rend(); ++it) {
            if (k <= 0) break;
            auto t = it->second;
            vector<string> v(t.begin(), t.end());
            if (k >= t.size()) {
                res.insert(res.end(), v.begin(), v.end());
            } else {
                res.insert(res.end(), v.begin(), v.begin() + k);
            }
            k -= t.size();
        }
        return res;
    }
};

下面这种解法是一种桶排序的思路,我们根据出现次数建立多个bucket,桶的个数不会超过单词的个数,在每个桶中,我们对单词按字符顺序进行排序。我们可以用个数组来表示桶,每一层中放一个集合,利用set的自动排序的功能,使其能按字母顺序排列。我们还是需要首先建立每个单词和其出现次数的映射,然后将其组成pair放入map种,map是从小到大排序的,这样我们倒序遍历所有的桶,这样取pair,就是次数最大的,每次取出一层中所有的单词,如果此时的k大于该层的单词个数,就将整层的单词加入结果res中,否则就取前K个就行了,取完要更更新K值,如果K小于等于0了,就break掉,返回结果res即可,参见代码如下:

解法三:

class Solution {

public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        vector<string> res;
        unordered_map<string, int> freq;
        vector<set<string>> v(words.size() + 1, set<string>());
        for (string word : words) ++freq[word];
        for (auto a : freq) {
            v[a.second].insert(a.first);
        }
        for (int i = v.size() - 1; i >= 0; --i) {
            if (k <= 0) break;
            vector<string> t(v[i].begin(), v[i].end());
            if (k >= t.size()) {
                res.insert(res.end(), t.begin(), t.end());
            } else {
                res.insert(res.end(), t.begin(), t.begin() + k);
            }
            k -= t.size();
        }
        return res;
    }
};

参考资料:

https://discuss.leetcode.com/topic/106861/o-nlog-k-priority-queue-c-code 

https://discuss.leetcode.com/topic/106868/clean-heap-based-solution-o-nlogk-time-and-o-n-space-16ms

本文转自博客园Grandyang的博客,原文链接:[LeetCode] Top K Frequent Words 前K个高频词

,如需转载请自行联系原博主。

时间: 2024-08-04 07:16:49

[LeetCode] Top K Frequent Words 前K个高频词的相关文章

[LeetCode] K Inverse Pairs Array K个翻转对数组

Given two integers n and k, find how many different arrays consist of numbers from 1 to n such that there are exactly k inverse pairs. We define an inverse pair as following: For ith and jth element in the array, if i < j and a[i] > a[j] then it's a

网站三天一大K 两天一小K究其原因何在

最近接到了一个客户的网站,遇到了一件让人特别郁闷的事,A5提供SEO诊断服务(http://seo.admin5.com/topic-seo.html )也有少时间了,在这期间更是给很多的客户网站做了诊断,也遇到了一些比较多的麻烦,但经过A5站长网SEO团队的共同努力之后也最终都一一顺利解决了,但这次遇到的事就有点麻烦了,特别让人匪夷所思,通过跟客户的沟通发现及A5 SEO诊断优化小组的诊断发现,该网站基本上是三天一大K,网站三天一大K,两天一小K,特别奇怪,不过所幸的是,一般被K之后第二天或是

ccccc-if (k==1) if (1==k) if(k=1)三者有什么区别,为什么没有if(1=k)

问题描述 if (k==1) if (1==k) if(k=1)三者有什么区别,为什么没有if(1=k) if (k==1) if (1==k) if(k=1)三者有什么区别,为什么没有if(1=k) 解决方案 k==1 1==k是一样的. k=1是赋值表达式,它也可以视作一个bool表达式,当k=0的时候是false,否则是true 1=k作为赋值表达式是不合法的. 解决方案二: 对于java来说: 楼主指定的k应该是一个int型变量,属于基本数据类型,基本数据类型在java内存的常量区,如果

10大K站原因归纳-K站事件总结

相比大家对之前的百度K站事件印像足够深刻吧?大量的站点被K,其中涉及医疗行业.淘宝客等站点,今天我为什么要提及这两个类型的站?(首先自我介绍下:我是FA团队的SEO优化组组长).因为太典型了,我们有必要仔细进行分析,然后吸取教训,保障自己的站点不被K. OK,我们开始今天的课程-网站被K的10个原因,后面会具体讲医疗行业站和淘宝客站. 1.优化过度,这一点我想做SEO的都太熟悉了,做到这个份上的都不应该会轻易犯这种低级的错误,说到底这就是搜索引擎的霸王条款,你必须被牵着鼻子走,不然你就会被K,没

k歌软件酷我k歌使用图文步骤

  现在,唱K是很多人纾解压力,休闲娱乐的一大方式.不少人都在问,k歌软件酷我k歌怎么使用?今天,小编就来跟大家分享k歌软件酷我k歌使用图文步骤. 方法/步骤 1:界面熟悉 酷我K歌的主界面包括了很多的常用工具,如:歌词MV.曲库.直播等: 2:排行榜 我们可以在排行榜界面找到近期最热门的歌曲,下面我们来看一下: 3:使用软件K歌 K歌是该软件特色功能了,我们可以点击界面右下角的--工具--K歌按钮 5:搜索歌曲 我们可以直接在搜索框中找我们想要的歌曲. 6:录制歌曲 找到我们需要的歌曲之后,我

context.key(k,v)中的k是自定义对象问题

问题描述 k如果是String的话,hadoop可以做到把相同的字符串合并成(a,1),(a,2),(a,3),(a,4)->(a,(1,2,3,4))像现在,我的k如果是个对象,hadoop能按照我定义的equals进行合并吗?就想这样((a1,b1,c1),1),((a1,b1,c1),2),((a1,b1,c1),3)->((a1,b1,c1),(1,2,3))待处理数据集:目前的错误结果:其实想要的结果是类似这样的:tomyuwen264tomshuxue300tomyingyu400

python实现k均值算法示例(k均值聚类算法)_python

简单实现平面的点K均值分析,使用欧几里得距离,并用pylab展示. 复制代码 代码如下: import pylab as pl #calc Euclid squiredef calc_e_squire(a, b):    return (a[0]- b[0]) ** 2 + (a[1] - b[1]) **2 #init the 20 pointa = [2,4,3,6,7,8,2,3,5,6,12,10,15,16,11,10,19,17,16,13]b = [5,6,1,4,2,4,3,1,

自然语言处理技术(NLP)在推荐系统中的应用

个性化推荐是大数据时代不可或缺的技术,在电商.信息分发.计算广告.互联网金融等领域都起着重要的作用.具体来讲,个性化推荐在流量高效利用.信息高效分发.提升用户体验.长尾物品挖掘等方面均起着核心作用.在推荐系统中经常需要处理各种文本类数据,例如商品描述.新闻资讯.用户留言等等.具体来讲,我们需要使用文本数据完成以下任务: 候选商品召回.候选商品召回是推荐流程的第一步,用来生成待推荐的物品集合.这部分的核心操作是根据各种不同的推荐算法来获取到对应的物品集合.而文本类数据就是很重要的一类召回算法,具有

[经典面试题][网易]数组分割

[题目] 任意2N个正整数,从其中选出N个整数,使得选出的N个整数和同剩下的N个整数之和的差最小. [来源] 网易 [分析] 假设数组A[1..2N]所有元素的和是SUM.模仿动态规划解0-1背包问题的策略. 从2N个数中找N个元素,有三种可能:大于Sum/2,小于Sum/2以及等于Sum/2.而大于Sum/2与小于等于Sum/2没区别,故可以只考虑小于等于Sum/2的情况. 令S(k, i)表示前k个元素中任意i个元素的和的集合.显然: S(k, 1) = {A[i] | 1<= i <=