347. 前 K 个高频元素
思路:方法1:这道题最开始能想到的方法就是堆,因为是要输出前K个高频元素,我自然想到了大顶堆,堆中保存一个个2元数组,即数字和其出现的频率,按照其频率大小排序,用到了lambda表达式:
class Solution {//求前 k 大,用小根堆,求前 k 小,用大根堆。
public int[] topKFrequent(int[] nums, int k) {
PriorityQueue<int[]> queue = new PriorityQueue<int[]>((o1,o2)->(o2[1]-o1[1]));//这是大顶堆,复杂度是 nlogn
Map<Integer,Integer> map = new HashMap<>();
for(int i:nums){
map.put(i,map.getOrDefault(i,0)+1);
}
for(int i:map.keySet()){
queue.add(new int[]{i,map.get(i)});
}
int[] res = new int[k];
for(int i=0;i<k;i++){
res[i] = queue.poll()[0];
}
return res;
}
}
方法2:但是这样的话,时间复杂度就刚好为O(nlogn),我能不能再让时间复杂度更小呢?由于只用输出前K个高频元素,那么我们的堆中是不是就可以只保存K个元素呢?自然就会想到小顶堆,因为小的元素再堆顶,直接poll出来即可,遍历所有的元素后,堆中自然就只有K个最大元素了:
class Solution {//求前 k 大,用小根堆,求前 k 小,用大根堆。
public int[] topKFrequent(int[] nums, int k) {
PriorityQueue<int[]> queue = new PriorityQueue<int[]>((o1,o2)->(o1[1]-o2[1]));//这是小顶堆,复杂度是 nlogK
Map<Integer,Integer> map = new HashMap<>();
for(int i:nums){
map.put(i,map.getOrDefault(i,0)+1);
}
int n = 0;
for(int i:map.keySet()){
//if(n == k) queue.poll();
queue.add(new int[]{i,map.get(i)});
n++;
if(n > k) queue.poll();
}
int[] res = new int[k];
int p = 0;
while(!queue.isEmpty()){
res[p++] = queue.poll()[0];
}
return res;
}
}
通过这两种方法,我们可以得出一个结论://求前 k 大,用小根堆,求前 k 小,用大根堆。
方法3:为了进一步降低时间复杂度,桶排序是个更好的选择(牺牲空间复杂度换取时间复杂度),第i个桶表示出现频率为i的元素,当然频率相同的元素可能并不只有一个,所以,每个桶中存放一个链表(因为链表是可变长度的),所有元素都存放在桶中后,我们就可以愉快的从后往前遍历啦,res数组填满K个元素即为我们的最终答案啦~~(●’◡’●)
class Solution {
public int[] topKFrequent(int[] nums, int k) {
ArrayList<Integer>[] buckets = new ArrayList[nums.length+1];
Map<Integer,Integer> map = new HashMap<>();
for(int i:nums){
map.put(i,map.getOrDefault(i,0)+1);
}
for(int i:map.keySet()){
if(buckets[map.get(i)] == null){
buckets[map.get(i)] = new ArrayList<>();
}
buckets[map.get(i)].add(i);
}
int[] res = new int[k];
int p = 0;
for(int i = buckets.length-1;i>=0;i--){
//if(p == k) return res;
if(buckets[i] == null) continue;//初始化为null
int size = buckets[i].size();
while(size>0){
res[p] = buckets[i].get(size-1);
p++;
if(p == k) return res;
size--;
}
}
return res;
}
}
以上就是三种解法啦,涉及对堆以及桶排序的比较经典的题。注意题目中“数组中前 k 个高频元素的集合是唯一的。“因此 while(size>0)中无需有if(p == k) return res 的判断。