题目描述
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
哈希表 + 堆排
先用哈希表进行频率统计,然后维护一个大小为k的小根堆,每次尝试往堆中插入一个元素。
若堆的大小不足k,则直接插入。否则,与堆顶比较,若堆顶比当前元素大,直接舍弃当前元素(堆顶已经是k个中最小的了,当前元素更小,则当前元素不肯能是前k大);若堆顶比当前元素小,则用当前元素替换堆顶,并向下调整
/**
* 11ms
**/
class Solution {
int[] heapVal;
int[] heapFreq;
int heapSize;
int heapSizeLimit;
public int[] topKFrequent(int[] nums, int k) {
heapVal = new int[k];
heapFreq = new int[k];
heapSize = 0;
heapSizeLimit = k;
// 堆中下标0~k-1,
// 小根堆
Map<Integer, Integer> freq = new HashMap<>();
for (int i : nums) freq.put(i, freq.getOrDefault(i, 0) + 1);
for (int key : freq.keySet()) insert(key, freq.get(key));
return heapVal;
}
private void insert(int val, int freq) {
if (heapSize == heapSizeLimit) {
// 已经有k个元素在堆里了, 直接比较堆顶
if (heapFreq[0] >= freq) return ; // 堆顶比当前元素大, 则当前元素无需插入
// 堆顶比当前元素小, 则需要替换掉堆顶
heapVal[0] = val;
heapFreq[0] = freq;
down(0);
return ;
}
// 堆里不足k个元素, 则直接插入到末尾, 并向上调整
heapVal[heapSize] = val;
heapFreq[heapSize] = freq;
up(heapSize);
heapSize++;
}
private void up(int i) {
while (i > 0 && heapFreq[(i - 1) / 2] > heapFreq[i]) {
swap((i - 1) / 2, i);
i = (i - 1) / 2;
}
}
private void down(int i) {
int min = i;
if (2 * i + 1 < heapSize && heapFreq[2 * i + 1] < heapFreq[min]) min = 2 * i + 1;
if (2 * i + 2 < heapSize && heapFreq[2 * i + 2] < heapFreq[min]) min = 2 * i + 2;
if (min != i) {
swap(min, i);
down(min);
}
}
private void swap(int i, int j) {
int t = heapVal[i];
heapVal[i] = heapVal[j];
heapVal[j] = t;
t = heapFreq[i];
heapFreq[i] = heapFreq[j];
heapFreq[j] = t;
}
}
哈希表 + 快排
同样先用哈希表进行频率统计。然后生成两个数组val[]
和freq[]
,分别存储值,以及值出现的频率。
随后手写一个快排,按照freq[]
从大到小排序。
每趟排序,都会将数组分为左侧较大的,和右侧较小的两部分。
每次查看左侧部分的长度leftLen
,
- 若
leftLen > k
, 则只需要递归的对左侧部分进行处理。 - 若
leftLen = k
, 直接结束。 - 若
leftLen < k
, 则左侧一定是前k大, 随后对右侧进行处理, 并传入k = k - leftLen
随后取val[]
的前k项, 作为答案返回即可
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for (int i : nums) map.put(i, map.getOrDefault(i, 0) + 1);
int n = map.size();
int[] val = new int[n];
int[] freq = new int[n];
int i = 0;
for (int key : map.keySet()) {
val[i] = key;
freq[i] = map.get(key);
// System.out.printf("val[%d] = %d, freq[%d] = %d\n", i, val[i], i, freq[i]);
i++;
}
quickSort(val, freq, 0, n - 1, k);
int[] ret = new int[k];
for (i = 0; i < k; i++) ret[i] = val[i];
return ret;
}
// 按照freq从大到小排序
private void quickSort(int[] val, int[] freq, int l, int r, int k) {
if (l >= r) return ;
int x = freq[l + r >> 1], i = l - 1, j = r + 1;
while (i < j) {
do i++; while (freq[i] > x);
do j--; while (freq[j] < x);
if (i < j) {
int t = freq[i];
freq[i] = freq[j];
freq[j] = t;
t = val[i];
val[i] = val[j];
val[j] = t;
}
}
int left = j - l + 1;
if (left == k) return ;
if (left > k) quickSort(val, freq, l, j, k);
if (left < k) quickSort(val, freq, j + 1, r, k - left);
}
}