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:
Follow up:
题目(本题与第347题如出一辙):给定一个非空字符串数组和一个目标值k,返回出现频率排在前k个的字符串(按照频率从大到小的顺序排列,如果频率相同则按首字母从小到大的顺序排列)。假设数组中的每个字符串只含有小写字母,且目标值k满足1<=k<=数组中不重复出现的元素总数。能否用时间复杂度是O(nlogk)、空间复杂度是O(n)的算法解决问题?
实现思路1:用HashMap统计每个字符串出现的频率,将所有不重复的字符串存进List并按字符串出现频率降序排序(如果频率相同则按字符串升序排序),最后取出List的前k个字符串就是结果。这种思路的时间复杂度是O(nlogn)、空间复杂度是O(n)。
class Solution {
public List topKFrequent(String[] words, int k) {
if (words == null) throw new IllegalArgumentException("argument is null");
Map map = new HashMap<>();
for (String s : words) {
if (!map.containsKey(s))
map.put(s, 1);
else
map.put(s, map.get(s) + 1);
}
List list = new ArrayList<>(map.keySet());
Collections.sort(list, new Comparator() {
public int compare(String s1, String s2) {
int freq1 = map.get(s1);
int freq2 = map.get(s2);
if (freq1 == freq2)
return s1.compareTo(s2);
else
return freq2 - freq1;
}
});
return list.subList(0, k);
}
}
实现思路2:维护一个容量为k的堆,堆的排序规则是按字符串出现频率升序排序(如果频率相同则按字符串降序排序)。具体来说,用HashMap统计每个字符串出现的频率,然后逐个将字符串加入堆中,当堆的容量大于k时,删除堆首字符串(当前出现频率最小的字符串)。遍历完所有字符串后,逐个取出堆首字符串并存进一个List,最后这个List的逆序就是题目结果。由于堆中始终只有k个字符串,因此这种思路的时间复杂度是O(nlogk)、空间复杂度是O(n)。
class Solution {
public List topKFrequent(String[] words, int k) {
if (words == null) throw new IllegalArgumentException("argument is null");
Map map = new HashMap<>();
List result = new ArrayList<>();
for (String s : words) {
if (!map.containsKey(s))
map.put(s, 1);
else
map.put(s, map.get(s) + 1);
}
PriorityQueue pq = new PriorityQueue(new Comparator() {
public int compare(String s1, String s2) {
int freq1 = map.get(s1);
int freq2 = map.get(s2);
if (freq1 == freq2)
return s2.compareTo(s1);
else
return freq1 - freq2;
}
});
for (String key : map.keySet()) {
pq.offer(key);
if (pq.size() > k)
pq.poll();
}
while (!pq.isEmpty())
result.add(pq.poll());
Collections.reverse(result);
return result;
}
}
实现思路3:借鉴第347题的求解思路,即长度为N的字符串数组中,每个字符最多出现N次、最少出现1次。因此创建并维护一个长度为N+1的数组A,A[i]是原数组中出现频率为i的字符串集合。具体来说,先用HashMap求出每个字符串的出现频率,然后将每个字符串按照其出现频率存进数组A中。按照题目要求,频率为i的所有字符串存进A[i]时按字符串升序排序。最后从数组A的末端遍历A非null集合,将集合中的字符串存进一个最终结果List,当List容量为k时就得到最终结果。这种思路的时间复杂度是O(nlogn),空间复杂度是O(n)。
class Solution {
public List topKFrequent(String[] words, int k) {
if (words == null) throw new IllegalArgumentException("argument is null");
int length = words.length;
Map map = new HashMap<>();
List[] array = new ArrayList[length + 1];
List result = new ArrayList<>();
for (String s : words) {
if (!map.containsKey(s))
map.put(s, 1);
else
map.put(s, map.get(s) + 1);
}
for (String s : map.keySet()) {
int freq = map.get(s);
if (array[freq] == null)
array[freq] = new ArrayList<>();
array[freq].add(s);
}
for (int freq = length; freq >= 1; freq--) {
List temp = array[freq];
if (temp == null) continue;
Collections.sort(temp);
int size = result.size();
if (temp.size() >= k - size) {
result.addAll(temp.subList(0, k - size));
break;
}
else
result.addAll(temp);
}
return result;
}
}
参考资料:
https://leetcode.com/problems/top-k-frequent-words/solution/