单调队列的操作
- 对头出队:head++ or deque.pollFirst();
- 队尾入队:++tail or deque.addLast()
(1):直接入队:++tail
(2):先删除再入队:tail—,++tail
滑动窗口最大值
【题目】给定一个长度为n的数组和一个大小为m的滑动窗口(0<m<=n),请找出所有滑动窗口里的最大值。例如,数组{2,6,4,9,8,5,5,2},滑动窗口大小为3,那么,一共存在6个滑动窗口,它们的最大值分别为:{6,9,9,9,8,5}。
import java.util.Arrays;import java.util.Scanner;//朴素实现方式class Main {//8 3//2,6,4,9,8,5,5,2//[6, 9, 9, 9, 8, 5]public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int m = scanner.nextInt();scanner.nextLine();String inputStr = scanner.nextLine();String[] strArr = inputStr.split(",");int[] nums = new int[n];for (int i=0; i<n; i++) {nums[i] = Integer.parseInt(strArr[i]);}System.out.println(Arrays.toString(windowMaxValue(nums, m)));}//实现该函数public static int[] windowMaxValue(int[] nums, int m) {int[] res = new int[nums.length-m+1];for (int i=m-1; i< nums.length; i++) {int temp = nums[i];for (int j=i-m+1; j<i; j++) {temp = Math.max(nums[j], temp);}res[i-m+1] = temp;}return res;}}
import java.util.Arrays;
import java.util.Scanner;
public class MultiPackagePro {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
scanner.nextLine();
String input = scanner.nextLine();
String[] inputs = input.split(",");
int[] nums = new int[n];
for (int i = 0; i < inputs.length; i++) {
nums[i] = Integer.parseInt(inputs[i]);
}
System.out.println(Arrays.toString(windowMaxValuePro(nums, n, m)));
}
//单调队列
public static int[] windowMaxValuePro(int[] nums, int n, int m) {
int[] res = new int[n-m+1];
int[] queue = new int[n]; //模拟队列,存储的是下标
int head = 0, tail = -1;
for (int i = 0; i < n; i++) {
//不在窗口内,队头出队
if (head<=tail && queue[head] < i-m+1) {
head++;
}
//判断后出队
while (head<=tail && nums[i]>=nums[queue[tail]]) {
tail--;
}
queue[++tail] = i;
if (i>=m-1) {
res[i-m+1] = nums[queue[head]];
}
}
return res;
}
}
LeetCode 239 滑动窗口最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
//单调队列:出队和入队
int n = nums.length;
int[] res = new int[n-k+1];
Deque<Integer> deque = new LinkedList<>();
for (int i=0; i<n; i++) {
//队头存储的下标不在窗口种
if (!deque.isEmpty() && i-k+1 > deque.getFirst()) {
deque.pollFirst();
}
//队尾元素比插入的元素小,所以队尾出来,没有价值
while (!deque.isEmpty() && nums[deque.getLast()] <= nums[i]) {
deque.pollLast();
}
//入队
deque.addLast(i);
if (i>=k-1) { //要弹出来了
res[i-k+1] = nums[deque.getFirst()];
}
}
return res;
}
}
连续子序列的最大和
【题目】给定一个长度为n的整数序列,请找出长度不超过m的连续子序列的最大和。例如数组{2,-3,5,2,-4,-1,8},m取3,那么长度不超过3的连续子序列的最大和为8.
【分析】设计长度为3的窗口,来模拟查找。
【技巧】前缀和。先把数组转化为前缀和s[],然后使用s[i]-min(s[j], j∈[i-m, i-1])求最大和,用滑动窗口找[i-m,i-1]的最小值即可。
//朴素的方式
import java.util.Scanner;
public class LongestSequentialSum {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
scanner.nextLine();
String string = scanner.nextLine();
String[] strArr = string.split(",");
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = Integer.parseInt(strArr[i]);
}
//前缀和
int[] sum = new int[n];
sum[0] = nums[0];
for (int i = 1; i < n; i++) {
sum[i] = sum[i-1] + nums[i];
}
System.out.println(maxSum(sum, n, m));
}
public static int maxSum(int[] nums, int n, int m) {
int res = nums[0];
for (int i = 1; i < n; i++) {
int minValue = Integer.MAX_VALUE;
for (int j=i-m; j<i; j++) {
if (j > 0) {
minValue = Math.min(minValue, nums[j]);
}
}
res = Math.max(res, nums[i] - minValue);
}
return res;
}
}
//单调队列寻找窗口最小值
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;
public class LongestSequentialSumPro {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
scanner.nextLine();
String string = scanner.nextLine();
String[] strArr = string.split(",");
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = Integer.parseInt(strArr[i]);
}
//前缀和
int[] sum = new int[n];
sum[0] = nums[0];
for (int i = 1; i < n; i++) {
sum[i] = sum[i-1] + nums[i];
}
System.out.println(maxSumPro(sum, n, m));
}
public static int maxSumPro(int[] nums, int n, int m) {
int res = nums[0];
//使用单调队列寻找最小值,此处窗口不包含当前值
Deque<Integer> deque = new LinkedList<>();
deque.addLast(0);
for (int i = 0; i < n; i++) {
//出队和入队
if (!deque.isEmpty() && deque.getFirst() < i-m) deque.pollFirst();
res = Math.max(res, nums[i]-nums[deque.getFirst()]);
while (!deque.isEmpty() && nums[deque.getLast()] > nums[i]) deque.pollLast();
deque.addLast(i);
}
return res;
}
}
