给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。
环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 。
子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], …, nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 。
示例 1:
输入:nums = [1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3
示例 2:
输入:nums = [5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10
提示:
n == nums.length
1 <= n <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
数组nums是一个环形数组,当最大子数组在内部时候,直接计算最大子数组和就行了,
如果最大子数组在两端的时候,我们只需要转换计算最小子数组和就行了,因为:
两端的数组和+内部的数组和=总的数组和
我们只需要计算内部的最小子数组和,就可以让两端的子数组和达到最大子数组和,因为总的数组和是一个固定不变的数字。
我们需要维护以下几个变量:
maxS=Integer.MIN_VALUE;maxF=0;minS=0;minF=0;
依次表示:最大子数组和,选择当前数字的最大和,最小数组和,和选择当前数字最小的和。
maxF是一个过渡值,遍历到当前元素值a的时候,
选择加不加前面的子数组的值,表示的是选择当前元素的最大的和
minF是一个过渡值,遍历到当前元素值a的时候,
选择加不加前面的子数组的值,表示的是选择当前元素的最小的和
一般来说这时候就可以直接返回Math.max(maxS,sum-minS)
了,但是存在需要特判的情况。
需要特判的情况的nums数组:
nums[-3,-2,-3]
nums=[−1,1,−1]
当数组如同这种的时候,sum和minS的大小相同,maxS在数组内部,是不会去到边界的
,如果直接返回:Math.max(maxS,sum-minS),那么第一个数组nums[-3,-2,-3]就会出错,返回0,而我们一应该返回maxS=-2,虽然第二种不会出错,但是我们针对这种情况sum和minS的大小相同,我们直接返回maxS(最大子数组和)即可。
class Solution {
public int maxSubarraySumCircular(int[] nums) {
int maxS=Integer.MIN_VALUE,maxF=0,minS=0,minF=0;
int sum=0;
for (int a:nums) {
//maxF是一个过渡值,遍历到当前元素值a的时候
// 选择加不加前面的子数组的值,表示的是选择当前元素的最大的和
//maxF是正数则要,负数则不要
maxF=Math.max(maxF,0)+a;
//maxF和maxS作比较
maxS=Math.max(maxF,maxS);
//minF是一个过渡值,遍历到当前元素值a的时候
//选择加不加前面的子数组的值,表示的是选择当前元素的最小的和
//maxF是负数则要,正数则不要
minF=Math.min(minF,0)+a;
minS=Math.min(minF,minS);
//计算数组的总和
sum+=a;
}
//判断是否存在minS==sum,然后返回正确答案
return minS==sum?maxS:Math.max(maxS,sum-minS);
}
}
时间复杂度:O(n), n为数组的长度,for循环遍历了一遍。
空间复杂度:O(1)。