优先级队列(leetcode347. 前 K 个高频元素)
优先级队列是一种特殊的队列,与普通队列不同的是,在优先级队列中每个元素都关联一个优先级,具有高优先级的元素先出队列。优先级队列通常使用堆来实现。
优先级队列中的元素可以是任意类型,但需要满足以下条件:
-
可以比较大小;
-
可以判断相等关系。
Java 中的 PriorityQueue 就是一个优先级队列,它实现了 Queue 接口,并使用了一个小根堆来存储元素。在 PriorityQueue 中,每次出队的元素都是优先级最高的元素。当添加一个新元素时,它会根据元素的优先级自动调整堆中元素的位置,保持堆的性质。
PriorityQueue 支持以下操作:
-
add(E e) / offer(E e):将元素 e 添加到队列中;
-
peek():获取队列中优先级最高的元素,但不删除该元素;
-
poll():获取并删除队列中优先级最高的元素;
-
remove(Object o):从队列中删除元素 o;
-
size():获取队列中元素的个数。
我们可以用优先级队列实现小顶堆,让队列保持k的大小,每次想要进来的和小顶堆的堆顶元素(就是优先级队列队头)比较,比堆顶大就进队,否则比较下一个。
本题里面我们可以用map收集每个数据的出现次数,然后用优先级队列进行比较,这样相对于快速排序,会方便很多。
例如
nums = [4,1,-1,2,-1,2,3] k = 2
在我们把nums按照key为数组元素,value为出现次数装进map中后
map:
key value
-
4 1
-
1 1
-
-1 2
-
2 2
-
3 1
因为k=2,所以先让key为4和1进队,此时的队列
que:
key value
-
4 1
-
1 1
key = -1尝试进队,和队头(key = 4)比较value(其实就是比较出现次数),发现2>1,故让队头出队,让key = -1进队,此时的队列
que:
key value
-
1 1
-
-1 2
key = 2尝试进队,和队头(key = 1)比较value(其实就是比较出现次数),发现2>1,故让队头出队,让key = 2进队,此时的队列
que:
key value
-
-1 2
-
2 2
key = 3尝试进队,和队头(key = -1)比较value(其实就是比较出现次数),发现1<2,故队列不动
此时结束循环,输出队列的所有key值。
代码
class Solution { public int[] topKFrequent(int[] nums, int k) { Map<Integer, Integer> map = new HashMap<>(); /*Comparator接口说明: * 返回负数,形参中第一个参数排在前面;返回正数,形参中第二个参数排在前面 * 对于队列:排在前面意味着往队头靠 * 对于堆(使用PriorityQueue实现):从队头到队尾按从小到大排就是最小堆(小顶堆), * 从队头到队尾按从大到小排就是最大堆(大顶堆)--->队头元素相当于堆的根节点 * */ PriorityQueue<int[]> que = new PriorityQueue<>((a,b)->a[1]-b[1]);//按照升序排列,最小的在队头,所以是小顶堆 for (int num : nums) { map.put(num,map.getOrDefault(num,0)+1); } for (Map.Entry<Integer, Integer> entry : map.entrySet()) { if (que.size() < k) { que.add(new int[]{entry.getKey(),entry.getValue()}); }else { if (entry.getValue() > que.peek()[1]) { que.poll(); que.add(new int[]{entry.getKey(),entry.getValue()}); } } } int[] res = new int[k]; int num = 0; for (int[] ints : que) { res[num++] = ints[0]; } return res; } }