leetcode347 —— n中topK && PriorityQueue(Heap) && Map遍歷
阿新 • • 發佈:2021-10-23
題目要求:求前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 = newint[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 }
進階的時間要求想不出來,於是看了題解,有了以下收穫:
題目最終需要返回的是前k個頻率最大的元素,可以想到藉助堆這種資料結構,對於k頻率之後的元素不用再去處理,進一步優化時間複雜度。
- 求前 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()}); } }