前 K 個高頻元素問題
作者:Grey
原文地址: 前 K 個高頻元素問題
題目描述
LeetCode 347. Top K Frequent Elements
思路
第一步,針對數組元素封裝一個數據結構
public class Node {
int v;
int t;
public Node(int value, int times) {
v = value;
t = times;
}
}
其中v
表示數組元素,t
表示數組元素出現的次數。
第二步,使用哈希表把每個元素的詞頻先存一下。其中key
是數組元素,value
是Node
類型,封裝了數組元素和次數。
Map<Integer, Node> freqMap = new HashMap<>();
for (int n : arr) {
if (freqMap.containsKey(n)) {
// 存在就把詞頻加一
freqMap.get(n).t++;
} else {
// 不存在就新建一個詞頻
freqMap.put(n, new Node(n, 1));
}
}
第三步,使用一個小根堆,按詞頻從小到大。我們需要將這個小根堆維持在K
個高頻元素。具體做法如下
如果堆未超過K
個元素,可以入堆;
如果堆已經到了K
個元素了,就看堆頂的元素出現的次數是否比即將要遍歷的元素出現的次數少,如果堆頂元素出現的次數比即將要遍歷的元素少,
說明即將遍歷的元素比堆頂元素更高頻,可以替換掉堆頂元素,將其入堆;
如果堆已經超過K
個元素了,那麼彈出元素,讓堆始終保持在K
個元素。
第四步,彈出堆中所有元素,即爲前K
個高頻元素。
完整代碼如下:
public static class Node {
// 值
public int v;
// 次數
public int t;
public Node(int value, int times) {
v = value;
t = times;
}
}
public static int[] topKFrequent(int[] arr, int k) {
if (arr == null || arr.length == 0 || arr.length < k) {
return null;
}
Map<Integer, Node> freqMap = new HashMap<>();
for (int n : arr) {
if (freqMap.containsKey(n)) {
freqMap.get(n).t++;
} else {
freqMap.put(n, new Node(n, 1));
}
}
// 字符種類沒有k個,無法得到結果
if (freqMap.size() < k) {
return null;
}
int[] ans = new int[k];
PriorityQueue<Node> topK = new PriorityQueue<>(k, Comparator.comparingInt(o -> o.t));
for (Map.Entry<Integer, Node> entry : freqMap.entrySet()) {
if (topK.size() <= k || topK.peek().t < entry.getValue().t) {
topK.offer(entry.getValue());
}
if (topK.size() > k) {
topK.poll();
}
}
int i = 0;
while (!topK.isEmpty()) {
ans[i++] = topK.poll().v;
}
return ans;
}