题目:
题意理解:
- 依据题意,可知是给定一个数组,且给定一个数值 k ,要求数组中重复元素最多的前 k 个元素。
解题思路:
- 最开始的想法很粗暴:遍历数组,且创建一个map集合,键来存储数组元素,值则存储对应键出现的频率。最后通过每遍历一次map集合来求出一个中中最大值,同时删去该键。
- 这种想法确实简单粗暴,但是这样的做法因为多次遍历map集合来取得最大值,他的算法时间复杂度肯定远大于 n*n 。而且看了看后面的提示:**你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。**所以,马上打消了这个念头。
- 既然问题出在了多次遍历 map 集合来求得最大值,那有什么另外的方法可以改进呢?
- 改变数据结构? -----> 优先队列 PriorityQueue
- PriorityQueue 是基于二叉堆原理的优先队列,队列用动态数组实现。而二叉堆有如下三个特点:
1.完全二叉树堆的根节点的优先级最大(即最大或最小)
2.父节点的优先级必定大于子节点
3.兄弟节点的优先级不确定谁大谁小 - 而二叉堆的用途也就是得出最值。优先队列 PriorityQueue 内置属性:Comparator实现优先队列排序的方法。
PriorityQueue<Integer> PQ = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
PriorityQueue<Integer> PQ2 = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
- 根据上面的分析,我们用排序的角度来确认优先队列的优势,第一个暴力想法是每次从中取出一个最大值,类似与选择排序,而优先队列采用的是堆的思想,所以类似于堆排序。
排序算法 | 平均时间复杂度 |
---|
选择排序 | O(N*N) |
堆排序 | O(NlogN) |
代码:
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
for(int num : nums){
if(map.containsKey(num)){
map.put(num,map.get(num)+1);
}else{
map.put(num,1);
}
}
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return map.get(o1) - map.get(o2);
}
});
for(Integer key : map.keySet()){
if(pq.size() < k){
pq.add(key);
}else if(map.get(key) > map.get(pq.peek())){
pq.poll();
pq.add(key);
}
}
int[] res = new int[k];
int i = 0;
while(!pq.isEmpty()){
res[i] = pq.poll();
i++;
}
return res;
}
执行结果: