优先队列(Priority Queue)
高频面试题
1.数据流中的第 K 大元素
🚀题目链接:LeetCode703.数据流中的第 K 大元素
题目:
设计一个找到数据流中第k
大元素的类(class)。注意是排序后的第k
大元素,不是第k
个不同的元素。请实现
KthLargest
类:
KthLargest(int k, int[] nums)
使用整数k
和整数流nums
初始化对象。int add(int val)
将val
插入数据流nums
后,返回当前数据流中第k
大的元素。示例:
输入: ["KthLargest", "add", "add", "add", "add", "add"] [[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]] 输出: [null, 4, 5, 5, 8, 8] 解释: KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]); kthLargest.add(3); // return 4 kthLargest.add(5); // return 5 kthLargest.add(10); // return 5 kthLargest.add(9); // return 8 kthLargest.add(4); // return 8
🍬C++ AC代码:
class KthLargest {
private:
int q_size;
priority_queue<int, vector<int>, greater<int> > q;
public:
KthLargest(int k, vector<int>& nums) {
q_size = k;
for (int i = 0; i < nums.size(); i++)
add(nums[i]);
}
int add(int val) {
if (q.size() < q_size)
q.push(val);
else if (val > q.top()) {
q.pop();
q.push(val);
}
return q.top();
}
};
✨Tips:
- ⭐C++的标准库中包含了优先队列这种数据结构:
priority_queue<T>
,当泛型T
仅为基本数据类型时,比如priority_queue<int>
,默认是大顶堆,即最先出队的是队列中最大的元素;而本题中我们要使用的是小顶堆,即最先出队的是队列中最小的元素,可以这样声明:priority_queue<int, vector<int>, greater<int> > q
。 - ⭐
priority_queue<int>
是大顶堆,等同于:priority_queue<int, vector<int>, less<int> > q
。 - ⭐注意泛型的尖括号
<>
如果两个嵌套在一起,中间要用空格隔开,原因是>>
与<<
是右移与左移操作符,某些旧版的编译器会报错。
☕Java AC代码:
class KthLargest {
private int k;
private PriorityQueue<Integer> q;
public KthLargest(int k, int[] nums) {
this.k = k;
this.q = new PriorityQueue<Integer>();
for (int i = 0; i < nums.length; i++)
add(nums[i]);
}
public int add(int val) {
if (q.size() < k)
q.add(val);
else if (val > q.peek()) {
q.poll();
q.add(val);
}
return q.peek();
}
}
✨Tips:
-
⭐Java中的优先队列默认是小顶堆,刚好满足本题的要求,并且优先队列的初始容量默认是为11。
-
⭐如果要声明大顶堆,需要在构造函数中传入一个比较器:
// 创建一个大顶堆,默认容量为 11 PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11, new Comparator<Integer>(){ @Override public int compare(Integer i1, Integer i2){ return i2-i1; } });
🍦Python AC代码:
class KthLargest(object):
def __init__(self, k, nums):
self.k = k
self.q = []
heapq.heapify(self.q)
for num in nums:
self.add(num)
def add(self, val):
if len(self.q) < self.k:
heapq.heappush(self.q, val)
elif self.q[0] < val:
heapq.heapreplace(self.q, val)
return self.q[0]
✨Tips:
- ⭐Python的优先队列在
heapq
这个库中,heapq.heapify()
可以原地把一个list
调整成堆,同时默认为最小堆。 - ⭐
heapq.heappop()
可以弹出堆顶,并重新调整。 - ⭐
heapq.heappush()
可以新增元素到堆中。 - ⭐
heapq.heapreplace()
可以替换堆顶元素。
2.滑动窗口最大值
🚀题目链接:LeetCode239.滑动窗口最大值
题目:
给你一个整数数组
nums
,有一个大小为k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k
个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1 输出:[1]
🍬C++ AC代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
priority_queue<pair<int, int> > q;
for (int i = 0; i < k; i++)
q.push({nums[i], i});
vector<int> res = {q.top().first};
for (int i = k; i < nums.size(); i++) {
q.push({nums[i], i});
while (q.top().second <= i - k) {
q.pop();
}
res.push_back(q.top().first);
}
return res;
}
};
✨Tips:
- ⭐本题要找最大值,使用大顶堆比较合适,并且C++的优先队列默认就是大顶堆。
- ⭐在移动窗口时,优先队列顶端元素可能已经被移除窗口,因此需要同时将下标
i
和数值nums[i]
同时放入优先队列,当队列顶端元素不在窗口内时就将其移除,直到队列顶端元素在窗口内。要将下标和数值绑定起来,C++中可以使用二元组pair
实现。 - ⭐优先队列默认以第一个元素作为权重,因此
pair
中两个元素的存放顺序很重要,first
存放的是数值nums[i]
,second
存放下标i
。
☕Java AC代码:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
PriorityQueue<int[]> q = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] != o2[1] ? o2[1] - o1[1] : o2[0] - o1[0];
}
});
for (int i = 0; i < k; i++) {
q.add(new int[]{i, nums[i]});
}
res[0] = q.peek()[1];
for (int i = k; i < nums.length; i++) {
q.add(new int[]{i, nums[i]});
while (q.peek()[0] <= i - k) {
q.poll();
}
res[i - k + 1] = q.peek()[1];
}
return res;
}
}
✨Tips:
- ⭐Java中没有像C++中的二元组来存储下标i和数值
nums[i]
,可以使用数组来代替,数组的第一个元素存放下标i
,第二个元素存放数值nums[i]
。并且Java中的优先队列默认是小顶堆,因此需要自定义队列的比较器,优先比较数组的第二个元素,且数值越大优先级越高。 - ⭐方法的返回值是
int[]
类型,需要确定好待遍历数组和待返回数组之间下标的对应关系。
🍦Python AC代码:
class Solution:
def maxSlidingWindow(self, nums, k):
n = len(nums)
q = [(-nums[i], i) for i in range(k)]
heapq.heapify(q)
res = [-q[0][0]]
for i in range(k, n):
heapq.heappush(q, (-nums[i], i))
while q[0][1] <= i - k:
heapq.heappop(q)
res.append(-q[0][0])
return res
✨Tips:
- ⭐Python的代码直接参照了力扣的官方题解,应该是最简洁的代码了。
- ⭐python没有可以直接使用的大顶堆。但是我们可以给列表中的元素加上负号,变成小顶堆,再在取数的时候再将其添加负号,就将小顶堆变成了大顶堆。
总结