本文首發於公衆號「五分鐘學算法」,是圖解 LeetCode 系列文章之一。java
我的網站:www.cxyxiaowu.comgit
今天分享的題目來源於 LeetCode 上第 347 號問題:前 K 個高頻元素。題目難度爲 Medium,目前經過率爲 56.9% 。github
給定一個非空的整數數組,返回其中出現頻率前 k 高的元素。算法
示例 1:數組
輸入: nums = [1,1,1,2,2,3], k = 2
輸出: [1,2]
複製代碼
示例 2:bash
輸入: nums = [1], k = 1
輸出: [1]
複製代碼
說明:數據結構
最簡單粗暴的思路就是 使用排序算法對元素按照頻率由高到低進行排序,而後再取前 k 個元素。ide
如下十種排序算法,任你挑選!優化
能夠發現,使用常規的諸如 冒泡、選擇、甚至快速排序都是不知足題目要求,它們的時間複雜度都是大於或者等於 O(n logn) ,而題目要求算法的時間複雜度必須優於 O(n log n) 。網站
題目最終須要返回的是前 k 個頻率最大的元素,能夠想到藉助堆這種數據結構,對於 k 頻率以後的元素不用再去處理,進一步優化時間複雜度。
具體操做爲:
代碼以下:
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
// 使用字典,統計每一個元素出現的次數,元素爲鍵,元素出現的次數爲值
HashMap<Integer,Integer> map = new HashMap();
for(int num : nums){
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
// 遍歷map,用最小堆保存頻率最大的k個元素
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return map.get(a) - map.get(b);
}
});
for (Integer key : map.keySet()) {
if (pq.size() < k) {
pq.add(key);
} else if (map.get(key) > map.get(pq.peek())) {
pq.remove();
pq.add(key);
}
}
// 取出最小堆中的元素
List<Integer> res = new ArrayList<>();
while (!pq.isEmpty()) {
res.add(pq.remove());
}
return res;
}
}
複製代碼
首先依舊使用哈希表統計頻率,統計完成後,建立一個數組,將頻率做爲數組下標,對於出現頻率不一樣的數字集合,存入對應的數組下標便可。
代碼實現以下:
//基於桶排序求解「前 K 個高頻元素」
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
List<Integer> res = new ArrayList();
// 使用字典,統計每一個元素出現的次數,元素爲鍵,元素出現的次數爲值
HashMap<Integer,Integer> map = new HashMap();
for(int num : nums){
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
//桶排序
//將頻率做爲數組下標,對於出現頻率不一樣的數字集合,存入對應的數組下標
List<Integer>[] list = new List[nums.length+1];
for(int key : map.keySet()){
// 獲取出現的次數做爲下標
int i = map.get(key);
if(list[i] == null){
list[i] = new ArrayList();
}
list[i].add(key);
}
// 倒序遍歷數組獲取出現順序從大到小的排列
for(int i = list.length - 1;i >= 0 && res.size() < k;i--){
if(list[i] == null) continue;
res.addAll(list[i]);
}
return res;
}
}
複製代碼