1. 程式人生 > >leetcode筆記_優先佇列經典問題(N中選出前M)

leetcode筆記_優先佇列經典問題(N中選出前M)

一個關於優先佇列的經典問題

在N個元素中選出前M個元素(N遠大於M時)

  • 思路一:對N的元素進行排序,然後選出前M個元素,時間複雜度為O(NlogN)(採用快排或歸併排序等高階排序演算法)
  • 思路二:使用優先佇列,可以將時間複雜度降低為O(NlogM)。具體實現:使用優先佇列維護M個元素,即每次向優先佇列中加入一個元素時,需要將當前佇列中的最小元素替換出去,保證佇列中的M個元素是當前遍歷過的元素中的最大的前M個。

347. 前K個高頻元素

給定一個非空的整數陣列,返回其中出現頻率前 高的元素。

示例 1:

輸入: nums = [1,1,1,2,2,3], k = 2
輸出: [1,2]

示例 2:

輸入: nums = [1], k = 1
輸出: [1]

說明:

  • 你可以假設給定的 總是合理的,且 1 ≤ k ≤ 陣列中不相同的元素的個數。
  • 你的演算法的時間複雜度必須優於 O(n log n) , 是陣列的大小。
class Solution {
    public List<Integer> topKFrequent(int[] nums, int k) {
              
        //將陣列放入一個記錄頻次的Map中
        TreeMap<Integer, Integer> map = new TreeMap<>();
        for(int num : nums){
            if(map.containsKey(num)){
                map.put(num, map.get(num)+1);
            }else{
                map.put(num, 1);
            }
        }
        
        
        //通過一個最多可容納K個元素的優先佇列來選出出現頻率前k高的k個元素
        //優先佇列在構造時,通過傳入的外部比較器Comparator 可以定義優先佇列裡元素型別的比較方法
        //這裡,優先佇列裡存放Integer, 傳入的外部比較器重新定義了比較兩個整型型別的方法,即比較它們出現的頻次
        //Java內建優先佇列的底層是最小堆,所以出隊的是根據比較器定義的最小元素
        PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>(){
            @Override
            public int compare(Integer a, Integer b){  //使用匿名內部類實現Comparator介面,重新定義了比較兩個整型型別的方法,即比較它們出現的頻次
                return map.get(a)-map.get(b);
            }
        });
        
        for(int num : map.keySet()){  
            if(pq.size()<k){
                pq.add(num);
            }else{
                if(map.get(num)>map.get(pq.peek())){  //優先佇列內只保持k個元素,入隊時需要和優先順序最高的元素((頻次)最小的元素)作比較
                    pq.remove();
                    pq.add(num);
                }
            }
        }
        
        //根據要求,返回一個List
        ArrayList<Integer> list = new ArrayList<>();
        while(!pq.isEmpty()){
            list.add(pq.remove());
        }
        
        return list;                                                  
    }
}