题目地址:
https://www.lintcode.com/problem/621/
给定一个长 n n n的数组 A A A,再给定两个正整数 k 1 ≤ k 2 k_1\le k_2 k1≤k2,求所有长度在 [ k 1 , k 2 ] [k_1,k_2] [k1,k2]的子数组中和最大的那个,返回最大子数组和。
先求前缀和数组 p p p,那么对于任意 i i i,实际上就是要求 p [ i − k 2 : i − k 1 ] p[i-k_2:i-k_1] p[i−k2:i−k1]中的最小值,这可以看成是定长滑动窗口求最值问题,可以用单调队列来做。开一个单调上升队列(存下标,单调性是针对数值的),对于某个 i i i,先将小于 i − k 2 i-k_2 i−k2的下标弹出,由于要求最小值,所以将大于等于 p [ i − k 1 ] p[i-k_1] p[i−k1]的队尾下标也弹出,最后将 i − k 1 i-k_1 i−k1入队尾,那么此时队头存的就是 [ i − k 2 , i − k 1 ] [i-k_2,i-k_1] [i−k2,i−k1]的最小值。代码如下:
import java.util.ArrayDeque;
import java.util.Deque;
public class Solution {
/**
* @param nums: an array of integers
* @param k1: An integer
* @param k2: An integer
* @return: the largest sum
*/
public int maxSubarray5(int[] nums, int k1, int k2) {
// write your code here
int n = nums.length;
if (n < k1) {
return 0;
}
Deque<Integer> dq = new ArrayDeque<>();
int[] preSum = new int[n + 1];
for (int i = 0; i < n; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
int res = Integer.MIN_VALUE;
for (int i = k1; i <= n; i++) {
if (!dq.isEmpty() && i - dq.peekFirst() > k2) {
dq.pollFirst();
}
while (!dq.isEmpty() && preSum[dq.peekLast()] >= preSum[i - k1]) {
dq.pollLast();
}
dq.offerLast(i - k1);
// 队头存的就是最小值。
res = Math.max(res, preSum[i] - preSum[dq.peekFirst()]);
}
return res;
}
}
时间复杂度 O ( n ) O(n) O(n),空间 O ( k 2 ) O(k_2) O(k2)。