LeetCode:Maximum Sum Circular Subarray

题目描述

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)
运行结果

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值