一,239. 滑动窗口最大值(单调队列)
单调队列有两个性质 :
//1, 同普通队列的先进先出。
//2, 从队头到队尾的大小顺序是单调递减 或者 单调递增的。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
MyQueue queue = new MyQueue();
int n = 0;
for(int i = 0; i < k; ++i){
queue.add(nums[i]);
}
res[n] = queue.front();
for(int i = k; i < nums.length; ++i){
//移出 原 窗口中的第一个元素
queue.poll(nums[i - k]);
queue.add(nums[i]);
++ n;
res[n] = queue.front();
}
return res;
}
}
class MyQueue{
Deque<Integer> deque = new LinkedList<>();
//此处add方法保证单调队列的大小顺序,单调递减。
public void add(int val){
while(!deque.isEmpty() && val > deque.peekLast()){
deque.removeLast();
}
deque.add(val);
}
//这个poll方法写法可能多样,不同的题型,可能使用不同的写法
public void poll(int val){
if(!deque.isEmpty() && deque.peek() == val){
deque.poll();
}
}
public int front(){
return deque.peek();
}
}
二,347. 前 K 个高频元素(优先级队列)
统计元素个数,往往就是使用Map进行统计。
此题统计完个数,要进行排序,如果转为数组去排序还是比较麻烦的。
直接使用优先级队列比较方便
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int[] res = new int[k];
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; ++i){
map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
}
//set集合映射项
Set<Map.Entry<Integer, Integer>> entrys = map.entrySet();
//优先级队列,小顶堆是前面的对象减后面的对象
//要留下最后 k 个最大的元素,用小顶堆,用大顶堆,会导致循环更新时大的元素被拉出队列了。
PriorityQueue<Map.Entry<Integer,Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
for(Map.Entry<Integer, Integer> entry : entrys){
queue.offer(entry);
if(queue.size() > k){
queue.poll();
}
}
for(int i = 0; i < k; ++i){
res[i] = queue.poll().getKey();
}
return res;
}
}
三,739. 每日温度(单调栈)
使用于一维数组,寻找某个元素的左边(或右边)第一个比这个元素大(或小)的位置。
往往采用这个方法
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] res = new int[temperatures.length];
//这里使用Stack比较慢,但是方法都是栈的指定方法,不会出问题。
//如果使用ArrayDeque会快一点,但是方法一定使用正确,否则会出现奇妙的错误,非常amazing。例如 :ArrayDeque如果下面使用add方法和poll方法。就会出错,因为这是队列的方法。因为ArrayDeque既有队列的方法,也有栈的方法。也可称为堆栈。
Stack<Integer> stack = new Stack<>();
stack.push(0);
for(int i = 1; i < temperatures.length; ++i){
if(temperatures[i] <= temperatures[stack.peek()]){
stack.push(i);
}else{
while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){
res[stack.peek()] = i - stack.pop();
}
stack.push(i);
}
}
return res;
}
}
四,496. 下一个更大元素 I (单调栈)
单调栈使用,要考虑使用单调递减还是单调递增。
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
int[] res = new int[nums1.length];
Arrays.fill(res, -1);
//map在降低时间复杂度方面很有效,将On * n可以降为O n
HashMap<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums1.length; ++i){
map.put(nums1[i], i);
}
Stack<Integer> stack = new Stack<>();
stack.push(0);
for(int i = 1; i < nums2.length; ++i){
if(nums2[i] <= nums2[stack.peek()]){
stack.push(i);
}else{
while(!stack.isEmpty() && nums2[i] > nums2[stack.peek()]){
if(map.containsKey(nums2[stack.peek()])){
//注意是map对应的 这个元素的值,对应的下标。
res[map.get(nums2[stack.peek()])] = nums2[i];
}
stack.pop();
}
stack.push(i);
}
}
return res;
}
}
五,503. 下一个更大元素 II(单调栈)
class Solution {
public int[] nextGreaterElements(int[] nums) {
int[] res = new int[nums.length];
Arrays.fill(res, -1);
Stack<Integer> stack = new Stack<>();
for(int i = 0; i < 2 * nums.length; ++i){
//实现循环的方法可以使用求余的方式,是一种很好的方法。
while(!stack.isEmpty() && nums[i % nums.length] > nums[stack.peek() % nums.length]){
res[stack.peek() % nums.length] = nums[i % nums.length];
stack.pop();
}
stack.push(i % nums.length);
}
return res;
}
}