Maximum Sum Circular Subarray 环形子数组的最大和
问题描述:
给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。
环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 n u m s [ ( i + 1 ) m o d n ] , n u m s [ i ] 的前一个元素是 n u m s [ ( i − 1 + n ) m o d n ] nums[(i + 1) \mod n] , nums[i] 的前一个元素是 nums[(i - 1 + n) \mod n] nums[(i+1)modn],nums[i]的前一个元素是nums[(i−1+n)modn]。
子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 n u m s [ i ] , n u m s [ i + 1 ] , . . . , n u m s [ j ] ,不存在 i < = k 1 , k 2 < = j 其中 k 1 m o d n = = k 2 m o d n nums[i], nums[i + 1], ..., nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 \mod n == k2 \mod n nums[i],nums[i+1],...,nums[j],不存在i<=k1,k2<=j其中k1modn==k2modn 。
n = = n u m s . l e n g t h 1 < = n < = 3 ∗ 1 0 4 − 3 ∗ 1 0 4 < = n u m s [ i ] < = 3 ∗ 1 0 4 n == nums.length\\ 1 <= n <= 3 * 10^4\\ -3 * 10^4 <= nums[i] <= 3 * 10^4 n==nums.length1<=n<=3∗104−3∗104<=nums[i]<=3∗104
分析
基于上一篇的优先队列,可以很明显的看出时间复杂度很高,在该问题规模下,是非常有可能TLE的,虽然在昨天的提交中还可以勉强AC,但是理论上这个时间复杂度是不太理想的,和之前的 O ( N ) O(N) O(N)的时间复杂度更是无法比。
所以补一个 单调队列的思路。
目标同样是要找在固定下标i左侧的最小前缀和。
优先队列的处理关键是排序,而单调队列是基于更加巧妙的队列维护。
如果在下标
i
i
i的左侧存在一个下标j处的前缀和是最小的。
此时子数组
[
j
,
i
]
[j,i]
[j,i]就是一个以i为右端点的最大和的子数组。
如果此时 下标
i
i
i的前缀和
p
r
e
s
u
m
i
presum_i
presumi大于下标
j
j
j的前缀和
p
r
e
s
u
m
j
presum_j
presumj,即
p
r
e
s
u
m
i
>
p
r
e
s
u
m
j
presum_i>presum_j
presumi>presumj.那么
p
r
e
s
u
m
i
presum_i
presumi 就不可能和
i
i
i右侧的其他下标
p
r
e
s
u
m
x
presum_x
presumx构成更大的子数组和,所以这个情况下,
p
r
e
s
u
m
i
presum_i
presumi 不需要被考虑。
反之, p r e s u m i < = p r e s u m j presum_i<=presum_j presumi<=presumj,此时的 p r e s u m i presum_i presumi 相比较 p r e s u m j presum_j presumj可以与右侧的 p r e s u m x presum_x presumx构造更大的子数组和,那么 p r e s u m i presum_i presumi 就应该被考虑,
所以在队列的尾部,用 p r e s u m i presum_i presumi与队尾的 p r e s u m presum presum进行比较,如果发现 p r e s u m i presum_i presumi更加小或者相等,就可以将队尾元素出队。直到遇到比 p r e s u m i presum_i presumi更小的或者队列空了。
代码
单调队列
class Solution {
public int maxSubarraySumCircular(int[] nums) {
int n = nums.length,sum =0,ans = Integer.MIN_VALUE;
// idx --- val
Deque<int[]> dq = new ArrayDeque();
dq.offerLast(new int[]{0,0});
for(int i=0;i<2*n-1;i++){
sum += nums[i%n];
while(!dq.isEmpty()
&&(i-dq.peekFirst()[0])>n-1){
dq.pollFirst();
}
if(dq.peekFirst()[0]==n)break;
if(!dq.isEmpty()){
ans = Math.max(ans,sum -dq.peekFirst()[1]);
}
while(!dq.isEmpty()&&dq.peekLast()[1]> sum){
dq.pollLast();
}
dq.offerLast(new int[]{i,sum});
}
return ans;
}
}
时间复杂度 O ( N ) O(N) O(N)
空间复杂度 O ( N ) O(N) O(N)
Tag
Array
Monotonic Queue