持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
最近一直在力扣刷题,也逐渐对各类题型有了自己的理解,所谓见招拆招,将自己的浅显经验分享一下,帮助更多在编程路上的朋友们。
环形子数组的最大和
给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。
环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 。
示例 1:
输入:nums = [1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3
示例 2:
输入:nums = [5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10
思路
子数组要求是数组连续的,不能改变数组的顺序,很容易想到使用动态规划,如果想不到,建议养成肌肉记忆T_T
动态规划方程很简单:
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
但是dp[n - 1]并不是最大的子数组,例如dp[n - 2] = 6, nums[n - 1] = -1,此时的dp[n - 1] = 5,故需要一个max变量存储子数组的最大值。
状态转移方程如下
for(int i = 1; i < n; i++) {
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
max = Math.max(dp[i], max);
}
以上为一般数组求子数组最大和方法,但该题要求环形数组的子数组,需要考虑到数组末尾与首部的关系,就算循环两次,也无法确定会不会出现首尾重复计算的情况,这个问题也困扰了我很久,最后还是要靠大神的解法解决,分享如下
分为两种情况,一种最大子数组首尾未成环,上文已讲述,如果最大子数组成环,则最大子数组的和等价于数组和减去最小子数组的和
还有一种情况需要考虑,如果数组元素全为负数时,最小子数组和就会等于数组和,此时sum - min == 0,故最后需要判断若最大值为负数时,改最大值即为子数组最大和。
题解
class Solution {
public int maxSubarraySumCircular(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
dp[0] = nums[0];
int max = nums[0];
int sum = nums[0];
for(int i = 1; i < n; i++) {
sum += nums[i];
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
max = Math.max(max, dp[i]);
}
int min = nums[0];
for(int i = 1; i < n; i++) {
dp[i] = Math.min(dp[i - 1] + nums[i], nums[i]);
min = Math.min(min, dp[i]);
}
return max < 0? max: Math.max(max, sum - min);
}
}