题目
Given a non-empty array of integers, return the k most frequent elements.
Example 1:
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]
Example 2:
Input: nums = [1], k = 1
Output: [1]
Note:
- You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
- Your algorithm's time complexity must be better than O(n log n), where n is the array's size.
十分钟尝试
没有思路,看了solution,记住下面一句话:
k = 1
the linear-time solution is quite simple. One could keep the frequency of elements appearance in a hash map and update the maximum element at each step.
When k > 1
we need a data structure that has a fast access to the elements ordered by their frequencies. The idea here is to use the heap which is also known as priority queue.
因为需要求top k出现频率的元素,我们需要一种数据结构能够快速根据频率找到对应的那些元素,所以桶排序可以,桶用一个数组表示,索引就是出现的次数,数组的每个元素都是一个list,用来存储这个频率的元素。最后倒序遍历数组到k为止,就可以找到频率最高的k个元素了。
除了桶排序,还有堆可以用。稍微分析堆。
正确解法-桶排序
看了思路,自己写完,代码易错点
1 初始化bucket ,类型第二个还是list,size应该为nums长度加1
2.top k的限制可以通过res的size判断
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
List<Integer> res=new ArrayList();
//List<Integer>[] bucket=new List<Integer>[nums.length];
List<Integer>[] bucket=new List[nums.length+1];
//计算频率
Map<Integer,Integer> frenqMap=new HashMap();
for(int i=0;i<nums.length;i++){
frenqMap.put(nums[i],frenqMap.getOrDefault(nums[i],0)+1);
}
//遍历频率map,放入bucket
for(Map.Entry entry: frenqMap.entrySet()){
int freq=(Integer)entry.getValue();
if(bucket[freq]==null){
bucket[freq]=new ArrayList();
}
bucket[freq].add((Integer)entry.getKey());
}
//倒序遍历bucket,找到top k,k的限制通过res的size限制
for(int i=bucket.length-1;i>=0&&res.size()<k;i--){
if(bucket[i]!=null&&bucket[i].size()>0){
res.addAll(bucket[i]);
}
}
return res;
}
}
正确解法-堆排序
使用堆排序,思路和上面一样,无非就是数据结构不一样,为什么用堆?因为堆类似于完全二叉树,但是有堆自己的性质,比如大根堆,根顶元素最大,所以每次我们取出根顶元素,然后调整堆,再取出根顶元素,一直取出k个,这样就能拿到top k的元素。
这里用大根堆,为了便于写代码,我们利用java里面提供的优先队列,其实就是堆的数据结构实现。
稍后我们再开篇讲解一下优先队列的原理,先看代码:
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
List<Integer> res=new ArrayList();
//大根堆,按照频率生成大根堆,所以堆顶必然是当前频率最高的元素
PriorityQueue<Map.Entry<Integer, Integer>> heap=new PriorityQueue<>((a,b)->(b.getValue()-a.getValue()));
//计算频率
Map<Integer,Integer> frenqMap=new HashMap();
for(int i=0;i<nums.length;i++){
frenqMap.put(nums[i],frenqMap.getOrDefault(nums[i],0)+1);
}
//遍历频率map,放入heap
for(Map.Entry entry: frenqMap.entrySet()){
heap.add(entry);
}
//倒序遍历bucket,找到top k,k的限制通过res的size限制
while(res.size()<k){
res.add(heap.poll().getKey());
}
return res;
}
}