题目描述
Given a circular array C of integers represented by A, find the maximum possible sum of a non-empty subarray of C.
Here, a circular array means the end of the array connects to the beginning of the array. (Formally, C[i] = A[i] when 0 <= i < A.length, and C[i+A.length] = C[i] when i >= 0.)
Also, a subarray may only include each element of the fixed buffer A at most once. (Formally, for a subarray C[i], C[i+1], …, C[j], there does not exist i <= k1, k2 <= j with k1 % A.length = k2 % A.length.)
Example
Input: [1,-2,3,-2]
Output: 3
Explanation: Subarray [3] has maximum sum 3
Input: [5,-3,5]
Output: 10
Explanation: Subarray [5,5] has maximum sum 5 + 5 = 10
Input: [3,-1,2,-1]
Output: 4
Explanation: Subarray [2,-1,3] has maximum sum 2 + (-1) + 3 = 4
Input: [3,-2,2,-3]
Output: 3
Explanation: Subarray [3] and [3,-2,2] both have maximum sum 3
Input: [-2,-3,-1]
Output: -1
Explanation: Subarray [-1] has maximum sum -1
Note
-30000 <= A[i] <= 30000
1 <= A.length <= 30000
解题思路
这道题目实际上是最大子数组问题,这类问题有以下属性:
1.如果数组包含所有非负数,那么问题就很简单了。最大子数组是整个数组。
2.如果该数组包含所有非正数,则解决方案是任何大小为1的子数组,其中包含该数组的最大值(如果允许,则为空子数组)。
3.几个不同的子阵列可能具有相同的最大和。
可以使用几种不同的算法技术解决此问题,包括蛮力,分而治之,动态编程和简化为最短路径。
1.暴力计算
这种方法实现比较简单,两个for循环就能够实现,这里就不实现了。
2.动态规划实现
在具体算法实现之前,我们应该对Kadane(卡丹)算法有所了解。
Kadane算法
详细了解及算法实现可以参考维基百科:Maximum subarray problem
大致思想就是:第i个元素之前(包括i)的最大子数组之和为B(i),那么B(i+1)=max(A(i+1), A(i+1)+B(i))。因为如果A(i+1)+B(i) 比 A(i+1) 小的话,那说明 B(i) 是负的,所以可以抛弃掉,从 A(i+1) 开始计算即可,该算法时间复杂度为O(n)。
Python实现
def max_subarray(numbers):
"""Find a contiguous subarray with the largest sum."""
best_sum = 0 # or: float('-inf')
best_start = best_end = 0 # or: None
current_sum = 0
for current_end, x in enumerate(numbers):
if current_sum <= 0:
# Start a new sequence at the current element
current_start = current_end
current_sum = x
else:
# Extend the existing sequence with the current element
current_sum += x
if current_sum > best_sum:
best_sum = current_sum
best_start = current_start
best_end = current_end + 1 # the +1 is to make 'best_end' exclusive
return best_sum, best_start, best_end
思路分析
这道题的特殊之处在于数组是环形的。我们可以把所有子数组的情况分成两种:
1.子数组没有跨越起点和终点
对于这种情况,我们可以使用 Kadane’s algorithm 完美解决。
2.子数组跨越了起点和终点
我们不妨换一种思路:最大子数组跨越了起点和终点,但整个数组剩余的部分却是连续且没有跨越起点和终点,同时整个剩余部分的和是最小的。因此我们可以继续使用 Kadane’s algorithm 得到最小子数组和,用整个数组的总和减去最小子数组和就是跨越了起点和终点的最大子数组和。
一般情况下,我们只需得到上述两个结果,选择其中的较大者返回。但注意要考虑数组中的元素全为负数的特殊情况,这时1的结果是数组中的最大元素,2的结果是0,我们需要返回1中的结果,时间复杂度为O(n)。
Python代码实现
class Solution:
def maxSubarraySumCircular(self, A: List[int]) -> int:
curmax, submax = 0, -(1<<31)
curmin, submin = 0, 1<<31
total = 0
for a in A:
curmax = max(curmax+a, a)
submax = max(submax, curmax)
curmin = min(curmin+a, a)
submin = min(submin, curmin)
total += a
if submax < 0:
return submax
else:
return max(submax, total-submin)