题解:环形数组的最大子数组和
问题描述
我们需要找到一个环形数组的最大子数组和,即找到一段连续的子数组(可能跨越数组的首尾连接)使得其和最大。
解决思路
环形数组的最大子数组和问题,可以分为两种情况讨论:
-
子数组没有跨越数组的首尾(普通子数组)。
- 这种情况下,问题退化为经典的 最大子数组和 问题,可以用 Kadane 算法 高效解决。
-
子数组跨越了数组的首尾。
- 这种情况下,最大子数组和可以表示为: [ \text{totalSum} - \text{minSubarraySum} ]
- totalSum 是数组的总和。
- minSubarraySum 是子数组的最小和。
- 这种情况下,最大子数组和可以表示为: [ \text{totalSum} - \text{minSubarraySum} ]
因此,我们需要分别计算:
- 普通的最大子数组和(Kadane 算法)。
- 数组的总和。
- 最小子数组和(Kadane 算法的变体)。
最后,结果为以下两者的最大值: [ \text{result} = \max(\text{maxSubarraySum}, \text{totalSum} - \text{minSubarraySum}) ]
特殊情况
如果数组中所有元素都为负数,跨越首尾的子数组和将等于 (\text{totalSum} - \text{minSubarraySum} = 0),这不合法。此时应直接返回普通的最大子数组和。
算法步骤
- 使用 Kadane 算法求解普通的最大子数组和(
maxSubarraySum
)。 - 计算数组的总和(
totalSum
)。 - 使用 Kadane 算法求解最小子数组和(
minSubarraySum
)。 - 如果
totalSum == minSubarraySum
(所有元素为负数),直接返回maxSubarraySum
。 - 否则返回: [ \max(\text{maxSubarraySum}, \text{totalSum} - \text{minSubarraySum}) ]
代码实现
cpp
#include <iostream>
#include <vector>
#include <algorithm>
// 使用 Kadane 算法计算最大或最小子数组和
int kadane(const std::vector<int>& nums, bool findMax) {
int current = nums[0], best = nums[0];
for (size_t i = 1; i < nums.size(); ++i) {
if (findMax) {
current = std::max(nums[i], current + nums[i]);
best = std::max(best, current);
} else {
current = std::min(nums[i], current + nums[i]);
best = std::min(best, current);
}
}
return best;
}
// 主函数:环形数组的最大子数组和
int solution(std::vector<int>& nums) {
int n = nums.size();
// Case 1: 最大子数组和(不考虑环形)
int maxSubarraySum = kadane(nums, true);
// Case 2: 数组的总和
int totalSum = 0;
for (int num : nums) {
totalSum += num;
}
// Case 3: 最小子数组和
int minSubarraySum = kadane(nums, false);
// Case 4: 判断是否需要考虑环形情况
if (totalSum == minSubarraySum) {
// 如果总和等于最小子数组和,说明所有元素为负数,只能返回 maxSubarraySum
return maxSubarraySum;
} else {
// 否则,返回普通最大子数组和或环形最大子数组和中的较大值
return std::max(maxSubarraySum, totalSum - minSubarraySum);
}
}
// 测试用例
int main() {
std::vector<int> nums1 = {-1, -2, 3, -2};
std::vector<int> nums2 = {-5, -3, 5};
std::vector<int> nums3 = {-3, -1, 2, -1};
std::vector<int> nums4 = {-2, -3, -1};
std::cout << (solution(nums1) == 3) << std::endl; // 输出:3
std::cout << (solution(nums2) == 5) << std::endl; // 输出:5
std::cout << (solution(nums3) == 2) << std::endl; // 输出:2
std::cout << (solution(nums4) == -1) << std::endl; // 输出:-1
return 0;
}
代码解析
-
Kadane 算法
- 用于求解最大或最小子数组和。
- 时间复杂度为 (O(n))。
-
特殊处理
- 当数组中所有元素均为负数时,
totalSum == minSubarraySum
,无法通过环形方式求解。这时直接返回maxSubarraySum
。
- 当数组中所有元素均为负数时,
-
结果计算
- 普通最大子数组和
maxSubarraySum
。 - 环形最大子数组和
totalSum - minSubarraySum
。 - 返回两者的较大值。
- 普通最大子数组和
复杂度分析
-
时间复杂度
- Kadane 算法求解最大和最小子数组各需 (O(n))。
- 计算总和需要 (O(n))。
- 总时间复杂度:(O(n))。
-
空间复杂度
- 使用常数额外空间。
- 总空间复杂度:(O(1))。
测试分析
示例 1
输入:nums = [-1, -2, 3, -2]
maxSubarraySum = 3
(子数组为[3]
)。totalSum = -2
。minSubarraySum = -4
(子数组为[-1, -2]
)。totalSum - minSubarraySum = 2
。- 输出:
max(3, 2) = 3
。
示例 2
输入:nums = [-5, -3, 5]
maxSubarraySum = 5
。totalSum = -3
。minSubarraySum = -8
。totalSum - minSubarraySum = 5
。- 输出:
max(5, 5) = 5
。
示例 3
输入:nums = [-3, -1, 2, -1]
maxSubarraySum = 2
。totalSum = -3
。minSubarraySum = -5
。totalSum - minSubarraySum = 2
。- 输出:
max(2, 2) = 2
。
示例 4
输入:nums = [-2, -3, -1]
maxSubarraySum = -1
。totalSum = -6
。minSubarraySum = -6
。totalSum == minSubarraySum
,返回 `maxSubarraySum =
-1
。
总结
-
问题拆解
- 普通子数组和问题使用 Kadane 算法解决。
- 环形子数组和问题通过
totalSum - minSubarraySum
转化。
-
关键点
- 需要处理特殊情况:所有元素为负数时,只能返回普通的最大子数组和。
-
算法效率
- 时间复杂度 (O(n)) 和空间复杂度 (O(1)) 确保算法能够高效处理大规模数组。
-
代码优势
- 简洁明了,易于扩展。
- 通过分情况处理,覆盖所有可能的输入场景。
此方法是一种优化的、通用的解决方案,可以很好地解决环形数组的最大子数组和问题。