1. 程式人生 > 其它 >leetcode347 —— n中topK && PriorityQueue(Heap) && Map遍歷

leetcode347 —— n中topK && PriorityQueue(Heap) && Map遍歷

題目要求:求前K個最頻繁出現的數字。

1.很容易想到,使用HashMap<Integer,Integer>來儲存<number,frequency>鍵值對

1         int n = nums.length;
2         Map<Integer, Integer> map = new HashMap<>(n);
3 
4         for (int num : nums) {
5             map.put(num, map.getOrDefault(num, 0) + 1);
6         }    

2.接下來非常自然的思路就是:

  • 對<number,frequency>按照frequency進行降序排序
  • 返回前k個<number,frequency>的number

  根據題目的黑字提示:“你可以按任意順序返回答案。”。可以計算出第K頻繁的數的frequency,接著遍歷map,返回所有frequency >= Kth的number即可:

 1         Integer[] temp = map.values().toArray(new Integer[map.size()]);
 2         Arrays.sort(temp);
 3         int[] ans = new
int[k]; 4 int KthCount = temp[temp.length - k], i = 0; 5 6 for (Map.Entry<Integer, Integer> kv : map.entrySet()) { 7 if (kv.getValue() >= KthCount) { 8 ans[i++] = kv.getKey(); 9 } 10 }

因為時間超過5%,想到了對map遍歷時是否可以直接遍歷map中的鍵值對,於是:

1         for (Map.Entry<Integer, Integer> kv : map.entrySet()) {
2             if (kv.getValue() >= KthCount) {
3                 ans[i++] = kv.getKey();
4             }
5         }

進階的時間要求想不出來,於是看了題解,有了以下收穫:

題目最終需要返回的是前kk個頻率最大的元素,可以想到藉助堆這種資料結構,對於kk頻率之後的元素不用再去處理,進一步優化時間複雜度。

  • 求前 k 大,用小根堆,求前 k 小,用大根堆
    •         PriorityQueue<int[]> minHeap = new PriorityQueue<>(k,
                      new Comparator<int[]>() {
                          @Override
                          public int compare(int[] o1, int[] o2) {
                              //升序
                              return o1[1] - o2[1];
                              //降序
                              return o2[1] - o1[1];
                          }
                      });

  • topk (前k大)用小根堆,維護堆大小不超過 k 即可。每次壓入堆前和堆頂元素比較,如果比堆頂元素還小,直接扔掉,否則壓入堆。檢查堆大小是否超過 k,如果超過,彈出堆頂。複雜度是 nlogk
  • 避免使用大根堆,因為你得把所有元素壓入堆,複雜度是 nlogn,而且還浪費記憶體。如果是海量元素,那就掛了。
        PriorityQueue<int[]> minHeap = new PriorityQueue<>(k,
                new Comparator<int[]>() {
                    @Override
                    public int compare(int[] o1, int[] o2) {
                        return o1[1] - o2[1];
                    }
                });
        
        int count = 0
        for (Map.Entry<Integer, Integer> kv : map.entrySet()) {
            //前K個鍵值對不需要判斷
            if (count++ < k) {
                minHeap.add(new int[]{kv.getKey(), kv.getValue()});
            } else if (kv.getValue() > minHeap.peek()[1]) {
                minHeap.poll();
                minHeap.add(new int[]{kv.getKey(), kv.getValue()});
            }
        }