这里写目录标题
239. 滑动窗口最大值
思路
此题比较困难 推荐视频讲解 单调队列 滑动窗口最大值【基础算法精讲 27】 --by 灵茶山艾府
代码
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
int[] ans = new int[n - k + 1];
ArrayDeque<Integer> q = new ArrayDeque<>();
for(int i = 0; i < n; i++){
while(!q.isEmpty() && nums[q.getLast()] <= nums[i]){
q.removeLast();
}
q.addLast(i);
if(i - q.getFirst() >= k){
q.removeFirst();
}
if(i >= k - 1){
ans[i-k+1] = nums[q.getFirst()];
}
}
return ans;
}
}
347. 前 K 个高频元素
代码
class Solution {
public int[] topKFrequent(int[] nums, int k) {
// 使用 HashMap 记录每个元素的出现频率
HashMap<Integer, Integer> map = new HashMap();
for(int num : nums){
map.put(num, map.getOrDefault(num, 0) + 1);
}
// 使用最小堆(PriorityQueue)根据元素出现频率进行排序
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
public int compare(Integer a, Integer b){
return map.get(a) - map.get(b);
}
});
// 遍历 HashMap 中的键(元素),维护最小堆的大小为 k
for(Integer key : map.keySet()){
if(pq.size() < k){
pq.add(key);
} else if(map.get(key) > map.get(pq.peek())){
// 如果当前元素的频率大于堆顶元素的频率,替换堆顶元素
pq.remove();
pq.add(key);
}
}
// 从最小堆中取出前 k 个元素,放入结果数组
int[] res = new int[k];
int index = 0;
while(!pq.isEmpty()){
res[index++] = pq.poll();
}
return res;
}
}
总结
做题过程中有一些疑问,求助chatGPT解答:
1.PriorityQueue
介绍
在Java中,PriorityQueue
是一个基于优先级堆的无界队列实现。它是Queue
接口的一个实现,但是与普通的队列不同,PriorityQueue
中的元素是按照它们的自然顺序或者通过构造函数提供的比较器来排序的。这意味着队列中的元素不是按照插入顺序存储的,而是按照它们的优先级进行排序。也就是说,只要加入PriorityQueue
的元素默认就会按照最小堆排序
以下是 PriorityQueue
的一些重要特性和使用方法:
1. 构造函数
PriorityQueue()
:创建一个初始容量为11的优先级队列,使用元素的自然顺序进行排序。PriorityQueue(int initialCapacity)
:创建一个具有指定初始容量的优先级队列,使用元素的自然顺序进行排序。PriorityQueue(int initialCapacity, Comparator<? super E> comparator)
:创建一个具有指定初始容量和比较器的优先级队列。
2. 方法
add(E e)
或offer(E e)
:将指定的元素插入此优先级队列。remove()
或poll()
:检索并删除队列的头部,即具有最高优先级的元素。peek()
:检索但不删除队列的头部,如果队列为空,则返回 null。size()
:返回队列中的元素数量。isEmpty()
:如果队列中没有元素,则返回 true。clear()
:移除队列中的所有元素。
3. 自定义比较器
如果要根据自定义的规则对元素进行排序,可以通过在构造函数中提供比较器来实现。比较器需要实现 Comparator
接口。
PriorityQueue<Integer> customPriorityQueue = new PriorityQueue<>(Collections.reverseOrder());
上述代码创建了一个降序排序的优先级队列。
4. 应用场景
PriorityQueue
在很多算法和问题中有广泛的应用,例如:
-
任务调度: 通过将任务按照优先级加入到
PriorityQueue
中,可以实现优先级调度。 -
Dijkstra算法: 用于找到图中节点之间的最短路径。
-
合并有序文件: 如果有多个有序文件,可以使用
PriorityQueue
来合并这些文件。 -
哈夫曼编码: 用于数据压缩。
总体而言,PriorityQueue
提供了一种方便的方式来管理具有不同优先级的元素,是在需要按照优先级处理元素的场景中的常见选择。
2.如何构建一个自定义比较器呢?
要在 PriorityQueue
中使用自定义比较器,你需要创建一个实现 Comparator
接口的类,并在创建 PriorityQueue
对象时将该比较器传递给构造函数。下面是一个简单的例子,演示如何使用自定义比较器来构建一个按照字符串长度降序排序的 PriorityQueue
:
import java.util.Comparator;
import java.util.PriorityQueue;
public class CustomComparatorExample {
public static void main(String[] args) {
// 创建自定义比较器
Comparator<String> lengthComparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
// 按照字符串长度降序排序
return Integer.compare(s2.length(), s1.length());
}
};
// 使用自定义比较器创建PriorityQueue
PriorityQueue<String> priorityQueue = new PriorityQueue<>(lengthComparator);
// 向PriorityQueue添加元素
priorityQueue.add("Apple");
priorityQueue.add("Banana");
priorityQueue.add("Orange");
priorityQueue.add("Grapes");
// 按照比较器定义的规则弹出元素
while (!priorityQueue.isEmpty()) {
System.out.println(priorityQueue.poll());
}
}
}
在上面的例子中,我们首先创建了一个实现了 Comparator
接口的匿名类 lengthComparator
,然后使用它来构造 PriorityQueue
对象。比较器定义了按照字符串长度降序排序,因此最终输出的结果是:
Banana
Orange
Grapes
Apple
这个例子演示了如何使用自定义比较器对元素进行排序,你可以根据需要定义不同的比较器来满足具体的排序要求。
3.只要加入PriorityQueue
的元素默认就会按照堆排序吗?
是的,PriorityQueue
类在Java中是实现了堆的数据结构,而且默认是最小堆。在最小堆中,堆顶的元素是最小的,而在最大堆中,堆顶的元素是最大的。对于 PriorityQueue
来说,默认是最小堆。
当你向 PriorityQueue
中添加元素时,它会自动维护堆的性质。具体来说:
-
插入操作: 当你插入一个新元素时,
PriorityQueue
会根据堆的性质进行调整,确保新插入的元素放在合适的位置,使得整个堆仍然满足最小堆的性质。 -
删除操作: 删除操作通常是删除堆顶元素,即最小的元素。删除后,
PriorityQueue
会将堆的最后一个元素移到堆顶,并通过向下调整(堆化)的方式保持最小堆的性质。
这种自动的堆调整使得 PriorityQueue
在一些场景中非常方便,比如用于实现优先级队列或者在一些排序相关的算法中。在这个代码中,通过使用 PriorityQueue
以及自定义的比较器,实现了按照元素出现频率的最小堆。