1. 程式人生 > >LeetCode刷題Medium篇Top K Frequent Elements

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;
        
    }
}