给出一个数组,找出前k个频率最高的元素,k是有效的
思路:
可以用一个hashMap统计每个数字出现的次数,
但是简单点的写法,不用HashMap,用数组cnt代替,数组下标表示数字,为了减小数组的长度,可以提前看数字的范围min~max,数组长度取max-min+1即可。
现在只找到了每个数字出现的次数,并不能按照次数从大到小排列,
因此还需要一个数组来做这件事情,按次数从大到小排列,还要能找到对应的数字。
这就需要数组的下标是次数,这样从右到左遍历就相当于按次数从大到小。
只需要在每个下标处保存对应的数字即可。
所以再用一个list数组,数组下标对应数字出现的次数,
这样,从右到左遍历数组 就相当于 从大到小遍历次数。
每个频率的list装入出现这个次数的数字。
i+min就是出现这个频率的数字,注意一定要+min,因为刚刚cnt的下标,也就是nums中的数字,从min~max映射到0~max-min+1了,也就是全减掉了min,现在要加回来。
再来理一遍,有两个数组,第一个,统计每个数字出现的次数。这时数组下标是数字,数组元素是数字出现的次数。
第2个,元素是list的数组,统计每个出现次数都有哪些数字(第一个的反向操作),相当于把第一个数组的元素当作下标,第一个数组的下标装入list.
最后,从右到左遍历第2个数组,取出list中的数字,直到取出k个为止。
public int[] topKFrequent(int[] nums, int k) {
if(nums.length == 1) return nums;
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
int maxCnt = 0;
int[] res = new int[k];
for(int num : nums) {
min = Math.min(min, num);
max = Math.max(max, num);
}
//统计每个数字出现的次数
int[] cnt = new int[max-min+1];
for(int num : nums) {
cnt[num-min]++;
maxCnt = Math.max(maxCnt, cnt[num-min]); //最大次数
}
//统计每个次数对应的数字
List<Integer>[] cntNum = new ArrayList[maxCnt+1];
for(int i = 0; i < cnt.length; i++) {
if(cnt[i] == 0) continue;
if(cntNum[cnt[i]] == null) cntNum[cnt[i]] = new ArrayList<Integer>();
cntNum[cnt[i]].add(i+min);
}
//按频率大小装入数字
for(int i = cntNum.length-1; i >= 0; i--) {
if(cntNum[i] == null) continue;
for(Integer num : cntNum[i]) {
res[--k] = num;
if(k <= 0) return res;
}
}
return res;
}