https://leetcode-cn.com/problems/top-k-frequent-elements/
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int[] res = new int[k];
HashMap<Integer,Integer> map = new HashMap<>();
for(int num:nums){
map.put(num,map.getOrDefault(num, 0) + 1);
}
//使用小顶堆,重写比较器,比较map.get(x)的大小,而不是直接比较x的大小。
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return map.get(o1) - map.get(o2);
}
});
//保持小顶堆的length为k
for(Integer key:map.keySet()){
if(pq.size() < k){
pq.add(key);
} else{
pq.add(key);
pq.remove();
}
}
//把小顶堆的key全部输出到数组中就是答案
for(int i = 0;i < res.length;i++) {
res[i] = pq.remove();
}
return res;
}
}
换种简洁的写法:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int[] res = new int[k];
HashMap<Integer,Integer> map = new HashMap<>();
for(int num:nums){
map.put(num,map.getOrDefault(num, 0) + 1);
}
//使用小顶堆,重写比较器,比较map.get(x)的大小,而不是直接比较x的大小。
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return map.get(o1) - map.get(o2);
}
});
//先构建小顶堆,再把多余的元素丢掉
for(Integer key:map.keySet())pq.add(key);
while(pq.size() > k) pq.remove();
int i = 0;
//把小顶堆的key全部输出到数组中就是答案
while(!pq.isEmpty()) res[i++] = pq.remove();
return res;
}
}
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int[] res = new int[k];
HashMap<Integer,Integer> map = new HashMap<>();
for(int num:nums){
map.put(num,map.getOrDefault(num, 0) + 1);
}
//使用大顶堆,重写比较器,比较map.get(x)的大小,而不是直接比较x的大小。
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return map.get(o2) - map.get(o1);
}
});
//先构建大顶堆
for(Integer key:map.keySet())pq.add(key);
int i = 0;
//取前k个元素
while(i < k) res[i++] = pq.remove();
return res;
}
}
一共三种写法,分别讲解。
首先题目要求返回前k个频率高的元素。
分三步
1.统计频率——使用hashmap
2.对频率排序
3.返回前k个元素
特别需要注意的是!!!
重写比较器的时候,比较的是map.get(o1)和o2,而不是o1和o2本身,因为传入的是keySet()(为了保证唯一性)。
使用堆排序,大顶堆的话返回前k个元素,小顶堆的话,remove掉dui.size() - k个元素,只保留下面的k个元素即可。
第一种写法,构建小顶堆的时候,小于k个元素时,直接添加,当等于k时,堆顶元素跟要加入的key做比较,map.get(key)更大的留下,一直保持在k个元素的小顶堆。
第二种写法,直接构建完小顶堆,去掉多余的元素。
第三种写法,构建大顶堆,只取前k个。
第一种空间消耗得最少,小数据量下三种写法差不多。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int[] result = new int[k];
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
// 根据map的value值正序排,相当于一个小顶堆
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
for (Map.Entry<Integer, Integer> entry : entries) {
queue.offer(entry);
if (queue.size() > k) {
queue.poll();
}
}
for (int i = k - 1; i >= 0; i--) {
result[i] = queue.poll().getKey();
}
return result;
}
}
再放一个Carl(代码随想录)写的
没搜过entrySet()之前,我觉得他这个写得有点绕,看不懂,搜完之后,卧槽牛逼。
entrySet一弄,小顶堆存的直接是key-value键值对,要啥有啥,可以可以!