LeetCode刷題Medium篇Top K Frequent Elements
題目
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;
}
}