问题描述
给定一个长度为 n 的环形整数数组 nums,我们需要找到这个数组中的非空子数组的最大可能和。环形数组意味着数组的首尾相连,即 nums[i] 的下一个元素是 nums[(i + 1) % n],而 nums[i] 的前一个元素是 nums[(i - 1 + n) % n]。需要注意的是,每个元素最多只能在子数组中出现一次,且子数组是连续的,可以跨越数组的末端连接到开头。
输入输出
- 输入:一个整数数组 nums。
- 输出:一个整数,表示环形数组中非空子数组的最大和。
算法思路
-
非环形数组的最大子数组和:首先,我们需要计算非环形数组的最大子数组和。这可以通过 Kadane 算法实现,该算法可以找出数组中连续子数组的最大和。
-
环形数组的最大子数组和:对于环形数组,我们考虑两种情况:
- 包含整个数组的子数组。
- 不包含整个数组的子数组,即至少跳过数组中的一个元素。
为了计算环形数组的最大子数组和,我们可以将数组中的每个元素取反,然后再次应用 Kadane 算法。这样做的原因是,如果我们取反数组中的元素,那么原本的最小子数组和就变成了最大子数组和。然后,我们将原始数组的总和与取反后数组的最大子数组和相加,得到的结果就是环形数组的最大子数组和。
-
特殊情况处理:如果环形数组的最大子数组和为0,说明所有元素都是负数,此时我们应该返回非环形数组的最大子数组和。
代码实现
public static int solution(int[] nums) {
int n = nums.length;
if (n == 0) return 0;
// 计算非环形数组的最大子数组和
int maxSum = kadane(nums);
// 计算环形数组的最大子数组和
int totalSum = 0;
for (int i = 0; i < n; i++) {
totalSum += nums[i];
nums[i] = -nums[i]; // 反转数组元素
}
int minSum = kadane(nums); // 计算反转数组的最大子数组和
int maxCircularSum = totalSum + minSum; // 环形数组的最大子数组和
// 如果环形数组的最大子数组和为0,说明所有元素都是负数,返回非环形数组的最大子数组和
return maxCircularSum > 0 ? Math.max(maxSum, maxCircularSum) : maxSum;
}
private static int kadane(int[] nums) {
int maxEndingHere = nums[0];
int maxSoFar = nums[0];
for (int i = 1; i < nums.length; i++) {
maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]);
maxSoFar = Math.max(maxSoFar, maxEndingHere);
}
return maxSoFar;
}
public static void main(String[] args) {
System.out.println(solution(new int[]{-1, -2, 3, -2}) == 3);
System.out.println(solution(new int[]{-5, -3, 5}) == 5);
System.out.println(solution(new int[]{-3, -1, 2, -1}) == 2);
System.out.println(solution(new int[]{-2, -3, -1}) == -1);
}
}
代码中定义了两个方法:
kadane:实现 Kadane 算法,用于计算非环形数组的最大子数组和。solution:实现上述算法思路,计算环形数组的最大子数组和。
测试样例
代码中提供了四个测试样例,分别对应不同的输入数组,并验证了算法的正确性。
总结
这个问题考察了对 Kadane 算法的理解和应用,以及如何处理环形数组的特殊性质。通过反转数组元素的技巧,我们可以将问题转化为求解非环形数组的最大子数组和问题,从而简化问题的解决过程。