题目描述:
示例:
提示:
n == nums.length
1 <= n <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
相似题目: 最大子数组和
解题思路:
方法一:动态规划
由于数组是环形的,所以最大子数组的形势会出现两种情况:
-
情况一:
最大子数组只有一段,即 [i, j] { i >= 0 && j < len && i < j}
-
情况二:
最大子数组出现两段, [0, i] 和 [j, len - 1] { i < j}
情况一解法:
针对情况一的解法和最大子数组和一样,同样使用动态规划。
-
定义数组dp = new int[len], 其中dp[i] 表示 以下标i结尾的最大子数组的和。
-
推导递推公式:
当遍历到下标 i 时,以下标 i 结尾的最大子数组的和应该为多少?
dp[i] 取决于 dp[i - 1] 和 nums[i],其中dp[i - 1] 表示 以下标 i - 1 结尾的最大子数组的和,所以dp[i] = dp[i - 1] + nums[i], 但是nums[i] 如果比dp[i - 1] + nums[i]还要大,那dp[i] = nums[i], 所以 dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])。
-
初始化数组dp:
通过递推公式可知dp[i] 是由前一个推导出来的,所以dp[0] 必须初始化,dp[0] = nums[0]。
-
确定遍历顺序:
从前往后遍历即可。
public int maxSumOfSubArray(int[] nums){
int len = nums.length;
int[] dp = new int[len];
dp[0] = nums[0];
int ans = dp[0];
for(int i = 1; i < len; i++){
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
ans = Math.max(dp[i], ans); // 更新结果。
}
return ans;
}
情况二解法:
我们需要记录[0, i] 区间的最大值,这个区间是从0开始的,比方说[0, 2]区间的最大值为10,如果[0, 3]区间的和小于10,那么[0, 3]区间的值为[0, 2]区间的值。
记录[0, i]区间的最大值可以在情况一中记录,如下:
public int maxSumOfSubArray(int[] nums){
int len = nums.length;
int[] dp = new int[len];
dp[0] = nums[0];
int ans = dp[0];
//=========记录[0, i]区间最大值===================
int[] leftSum = new int[len];
leftSum[0] = nums[0];
int preSum = nums[0];
//==============================================
for(int i = 1; i < len; i++){
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
ans = Math.max(dp[i], ans); // 更新结果。
//========记录[0, i]区间最大值==================
preSum += nums[i]; // 记录[0, i] 区间的和。
leftSum[i] = Math.max(leftSum[i - 1], preSum);
}
return ans;
}
接下来,倒序遍历nums数组,记录[j, len - 1]的和。[j, len - 1]的和加上 leftSum[j - 1]就是情况二最大子数组的和。
完整代码如下:
public int maxSubarraySumCircular01(int[] nums) {
int len = nums.length;
int ans = nums[0];
int[] dp = new int[len];
dp[0] = nums[0];
int[] leftSum = new int[len];
leftSum[0] = nums[0];
int preSum = nums[0];
for (int i = 1; i < len; i++) {
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
ans = Math.max(dp[i], ans);
preSum += nums[i];
leftSum[i] = Math.max(leftSum[i - 1], preSum);
}
int backSum = 0; // 记录[j, len - 1]的和
for (int j = len - 1; j > 0; j--) {
backSum += nums[j];
ans = Math.max(ans, backSum + leftSum[j - 1]);
}
return ans;
}