如果用堆排序的话,这题和 数组中的第k个最大元素 是一模一样的。无非是把结点元素换成键值对而已。
需要注意的是,要取出所有k个元素,所以要加等于号,i >= list.size() - k,与215题不同:
// 取出前 k 个元素
for (int i = heapSize - 1, j = 0; i >= list.size() - k; i--, j++) {
// 取堆顶元素
res[j] = list.get(0).getKey();
// 用最后一个叶子结点替补堆顶元素
Collections.swap(list, 0, i);
// 并调整大根堆
maxHeapify(list, 0, --heapSize);
}
完整代码:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
// 使用哈希表统计每个数字出现的次数
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
// 将 Set 转换为 List
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(entries);
// 对 list 建立大根堆
int heapSize = list.size();
buildMaxHeap(list, heapSize);
int[] res = new int[k];
// 取出前 k 个元素
for (int i = heapSize - 1, j = 0; i >= list.size() - k; i--, j++) {
// 取堆顶元素
res[j] = list.get(0).getKey();
// 用最后一个叶子结点替补堆顶元素
Collections.swap(list, 0, i);
// 并调整大根堆
maxHeapify(list, 0, --heapSize);
}
return res;
}
public void buildMaxHeap(List<Map.Entry<Integer, Integer>> list, int heapSize) {
// 从最后一个非叶子结点开始建堆
for (int i = heapSize / 2 - 1; i >= 0; i--) {
maxHeapify(list, i, heapSize);
}
}
public void maxHeapify(List<Map.Entry<Integer, Integer>> list, int i, int heapSize) {
// 准备好两个指针:结点 i 的左孩子和右孩子
int left = i * 2 + 1;
int right = i * 2 + 2;
int largest = i;
// 写好递归出口,并判断 i 和 left 谁更大
if (left < heapSize && list.get(left).getValue() > list.get(i).getValue()) {
largest = left;
}
// 写好递归出口,并判断 largest 和 right 谁更大
if (right < heapSize && list.get(right).getValue() > list.get(largest).getValue()) {
largest = right;
}
// 如果 largest 是 left 和 right 之一
if (largest != i) {
// 使用 Collections.swap() 交换
Collections.swap(list, i, largest);
// 调整 largest 的子结点
maxHeapify(list, largest, heapSize);
}
}
}