引言
给一个环形的整数数组,求解最大的非空子数组和。
例题
算法描述
先不考虑环的情况,那么将会是经典的最大子数组和的问题,特殊之处在于需要排除掉空数组的情况,用Kadane算法解决即可,这里不再赘述。
考虑环的情况,即最大子数组出现在头尾相接的数组中,这种情况应该如何解决呢?
这里可以用逆向思维来思考,如果最大子数组在头尾相接的数组中,那么就相当于从整个数组中“挖”掉了中间的最小子数组(不包含头尾元素),即最大子数组和转化为整个数组的和减去最小子数组和。按照这个思路,本题只需同时计算最大子数组和以及最小子数组和,比较两种情况下答案的大小即可。但需要考虑特殊情况,排除掉空数组,第一种情况比较容易处理,第二种情况需要判断最小子数组是否是整个数组。
可以通过最小子数组和是否等于整个数组之和来判断,如果等于则排除掉。证明这种判断方法的正确性,用反证法比较容易。考虑出现一个最小子数组等于整个数组之和的情况,如果它的左右不全为0,则左右数组之和为一正一负,最小子数组加上和为负的数组,数组和更小,与最小子数组矛盾,如果它的左右全为0,那么表示最大子数组和是0的情况可以取到,但这种情况已经在算最大子数组时包含到了,所以排除掉也无所谓。
代码实现
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int ans = 0, tot = 0;
int minans = 0;
int n = nums.size();
int f, g; //f存储最大,g存储最小
tot = ans = minans = f = g = nums[0];
for (int i = 1; i < n; i++) {
f = max(f, 0) + nums[i];
ans = max(ans, f);
g = min(g, 0) + nums[i];
minans = min(minans, g);
tot += nums[i];
}
if (minans != tot) {
ans = max(ans, tot - minans);
}
return ans;
}
};
评注
- Kadane算法可以优化空间到O(1),无需存储O(n)
- 本题用逆向思维去解决环的问题,可以作为一种特殊的处理环相关问题的参考思路
- 另外,反证法也是证明算法思路正确性的一种常见方法