给一个单词列表,求出这个列表中出现频次最高的K个单词。
注意事项
你需要按照单词的词频排序后输出,越高频的词排在越前面。如果两个单词出现的次数相同,则词典序小的排在前面。
样例
给出单词列表:
[
"yes", "lint", "code",
"yes", "code", "baby",
"you", "baby", "chrome",
"safari", "lint", "code",
"body", "lint", "code"
]
如果 k =3
, 返回["code", "lint", "baby"]
。
如果 k =4
, 返回["code", "lint", "baby", "yes"]
。
挑战
用 O(nlogk)的时间和 O(n) 的额外空间完成。
如果你能够用 O(n) 的时间和 O(k) 的额外空间,那就更好不过了。
代码
class Pair {
String key;
int value;
Pair(String key, int value) {
this.key = key;
this.value = value;
}
}
public class Solution {
/**
* @param words an array of string
* @param k an integer
* @return an array of string
*/
private Comparator<Pair> pairComparator = new Comparator<Pair>() {
public int compare(Pair left, Pair right) {
// return 正数说明左边单词出现的频次高
if (left.value != right.value) {
return left.value - right.value;
}
// 频次相同时,如果return 1说明左边词序小,把left.key放前面
return right.key.compareTo(left.key);
}
};
public String[] topKFrequentWords(String[] words, int k) {
if (k == 0) {
return new String[0];
}
// 统计每个单词出现的频次
HashMap<String, Integer> counter = new HashMap<>();
for (String word : words) {
if (counter.containsKey(word)) {
counter.put(word, counter.get(word) + 1);
} else {
counter.put(word, 1);
}
}
PriorityQueue<Pair> Q = new PriorityQueue<Pair>(k, pairComparator);
for (String word : counter.keySet()) {
Pair peak = Q.peek();
Pair newPair = new Pair(word, counter.get(word));
if (Q.size() < k) {
Q.add(newPair);
// newPair出现频次高或者词序小时,取代最小堆的堆顶元素
} else if (pairComparator.compare(newPair, peak) > 0) {
Q.poll();
Q.add(newPair);
}
}
String[] result = new String[k];
int index = 0;
while (!Q.isEmpty()) {
result[index++] = Q.poll().key;
}
// reverse
// 第一个和倒数第一个交换,第二个和倒数第二个交换,依次类推
for (int i = 0; i < index / 2; i++) {
String temp = result[i];
result[i] = result[index - i - 1];
result[index - i - 1] = temp;
}
return result;
}
}
CompareTo用法
先读取出字符串的第一个“字母”进行比较,比较的方法是ascii码表的值(字符所对应的十进制值),如果前面的大那么返回1,后面的大返回-1;此位置相同,继续比较下一位,直到最后一位,如果都相同的话,就返回0;