核心难点在于数组是环形的 —— 最大子数组可能是「普通的连续子数组」,也可能是「跨数组首尾的环形子数组」。 环形数组的最大子数组和只有两种可能:
- 情况 1:最大子数组是「非环形」的(和普通的最大子数组和一样),比如
[1,-2,3,-2]的最大子数组是[3]; - 情况 2:最大子数组是「环形」的(跨首尾),比如
[5,-3,5]的最大子数组是[5(末尾),5(开头)],和为 10。
关键推导:
- 情况 1 的解:用经典的「Kadane 算法」求普通最大子数组和(记为
max_normal); - 情况 2 的解:数组总和
total- 最小子数组和(记为min_sub)(因为「跨首尾的最大和」= 总和 - 中间那段最小的子数组和); - 最终结果:取
max(max_normal, total - min_sub); - 边界特例:如果数组全是负数(
max_normal < 0),则直接返回max_normal(因为此时total - min_sub = 0,但实际最大子数组和是数组中最大的那个负数)。
经典 Kadane 算法回顾(求普通最大子数组和)
Kadane 算法核心:遍历数组,维护「当前子数组和」,如果当前和为负,就重置为当前元素(因为负数会拉低后续和),同时更新全局最大值。
// 求普通最大子数组和
int kadane_max(vector<int>& nums) {
int cur_max = nums[0], max_sum = nums[0];
for (int i = 1; i < nums.size(); ++i) {
cur_max = max(nums[i], cur_max + nums[i]);
max_sum = max(max_sum, cur_max);
}
return max_sum;
}
// 求最小子数组和(Kadane变种)
int kadane_min(vector<int>& nums) {
int cur_min = nums[0], min_sum = nums[0];
for (int i = 1; i < nums.size(); ++i) {
cur_min = min(nums[i], cur_min + nums[i]);
min_sum = min(min_sum, cur_min);
}
return min_sum;
}
完整代码实现
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int n = nums.size();
if (n == 1) return nums[0]; // 边界:单元素数组直接返回
// 1. 计算数组总和
int total = 0;
for (int num : nums) total += num;
// 2. 求普通最大子数组和(情况1)
int cur_max = nums[0], max_normal = nums[0];
// 求最小子数组和(用于情况2)
int cur_min = nums[0], min_sub = nums[0];
for (int i = 1; i < n; ++i) {
// 更新最大子数组和
cur_max = max(nums[i], cur_max + nums[i]);
max_normal = max(max_normal, cur_max);
// 更新最小子数组和
cur_min = min(nums[i], cur_min + nums[i]);
min_sub = min(min_sub, cur_min);
}
// 3. 处理全负数的情况:如果max_normal < 0,说明所有元素都是负数,直接返回max_normal
if (max_normal < 0) {
return max_normal;
}
// 4. 情况2:环形最大和 = 总和 - 最小子数组和
int max_circular = total - min_sub;
// 5. 最终结果取两种情况的最大值
return max(max_normal, max_circular);
}
};
关键细节解释
-
为什么环形和 = 总和 - 最小子数组和? 比如数组
[5,-3,5],总和是 7,最小子数组和是 - 3,环形和 = 7 - (-3)=10(对应子数组[5,5]);本质是:环形子数组是「去掉中间一段最小的子数组」,剩下的首尾拼接就是最大的环形和。 -
全负数的边界处理? 比如数组
[-3,-2,-1],max_normal = -1(最大的单个元素),total - min_sub = -6 - (-6) = 0,但 0 并不是合法的子数组和(子数组不能为空),所以此时直接返回max_normal。 -
时间 / 空间复杂度:
- 时间复杂度:O (n)(仅遍历数组一次);
- 空间复杂度:O (1)(仅用常数变量)。