ps:要求 算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
解法一:粗暴排序法
最简单粗暴的思路就是 使用排序算法对元素按照频率由高到低进行排序,然后再取前 kk 个元素。
以下十种排序算法,任你挑选!
可以发现,使用常规的诸如 冒泡、选择、甚至快速排序都是不满足题目要求,它们的时间复杂度都是大于或者等于 O(n logn)O(nlogn),而题目要求算法的时间复杂度必须优于 O(n log n)O(nlogn)。
复杂度分析
时间复杂度:O(nlogn)O(nlogn),nn 表示数组长度。首先,遍历一遍数组统计元素的频率,这一系列操作的时间复杂度是 O(n)O(n);接着,排序算法时间复杂度为 O(nlogn)O(nlogn);因此整体时间复杂度为 O(nlogn)O(nlogn)。
空间复杂度:O(n)O(n),最极端的情况下(每个元素都不同),用于存储元素及其频率的 Map 需要存储 nn 个键值对。
解法二:最小堆
题目最终需要返回的是前 kk 个频率最大的元素,可以想到借助堆这种数据结构,对于 kk 频率之后的元素不用再去处理,进一步优化时间复杂度。
具体操作为:
借助 哈希表 来建立数字和其出现次数的映射,遍历一遍数组统计元素的频率
维护一个元素数目为 kk 的最小堆
每次都将新的元素与堆顶元素(堆中频率最小的元素)进行比较
如果新的元素的频率比堆顶端的元素大,则弹出堆顶端的元素,将新的元素添加进堆中
最终,堆中的 kk 个元素即为前 kk 个高频元素
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);
}
PriorityQueue<Integer> heap=new PriorityQueue<Integer>((n1,n2)->
map.get(n1)-map.get(n2)
);
for(int j:map.keySet()){
heap.add(j);
if(heap.size()>k){
heap.poll();
}
}
int[] result=new int[k];
for(int a=k-1;a>=0;a--){
result[a]=heap.poll();
}
return result;
}
}