求数组中的子数组之和的最大值 (python)

一个有N个整数元素的一维数组(A[0],A[1],...,A[n-1]),那么这个数组的子数组之和的最大值是什么呢?

理解题意:

  1. 题目所说的子数组是连续的。
  2. 题目只需要求和,并不需要返回子数组的具体位置。
  3. 数组的元素是整数,所以数组可能会出现正整数、零、负整数。

解法 1

使用前n项和的算法来做:

nums = [1,-2,3,5,-3,2]
n = len(nums)
sum = [0]*(n+1)
for i in range(n):
    sum[i+1] = sum[i] + nums[i]
maximum = -2**31
for i in range(n,0,-1):
    for j in range(i-1,-1,-1):
        if sum[i] - sum[j] > maximum:
            maximum = sum[i]-sum[j]

print(maximum)

优化:

nums = [1,-2,3,5,-3,2]
n, maximum =len(nums), -2**31
for i in range(n):
    sum = 0
    for j in range(i,n):
        sum += nums[j]
        if sum > maximum:
            maximum = sum
print(maximum)

解法 2

将所给的数组(A[0],...,A[n-1])分成长度相同的两段数组(A[0],...,A[n/2-1])和(A[n/2],...A[n-1]),分别求出这两段数组各自的最大子段和,则原数组的最大子段和为以下三种情况的最大值:

  1. A[0],...,A[n-1]最大字段和和A[0],...,A[n/2-1]最大字段和相同。
  2. A[0],...,A[n-1]最大字段和和A[n/2],...A[n-1]最大字段和相同。
  3. A[0],...,A[n-1]最大字段和跨越其中间两个元素A[n/2-1]到A[n/2]。

该算法的时间复杂度为:O(N*logN)。

解法 3 

从方法2中可以得到提示:可以考虑数组的第一个元素A[0],以及最大的一段数组(A[i],...,A[j])之间的关系有以下三种情况:

  1. 当0=i=j时,元素A[0]本身构成和的最大一段。
  2. 当0=i<j时,和最大的一段以A[0]开始。
  3. 当0<i时,和最大的一段与A[0]没有关系。

从上面的三种情况可以看出,可以将一个大问题(N个元素数组)转化为一个较小的问题(n-1个元素数组)。假设已经知道(A[1],...,A[n-1])中和最大的一段数组之和为All[1],并已经知道(A[1],..,A[n-1])中包含A[1]的和最大的一段数组为Start[1]。那么根据上述分析,我们可以得到(A[0],...,A[n-1])中最大子数组和为max(A[0],A[0]+Start[1],All[1])。那么根据这个规律我们可以很轻松的从数组末端节点开始遍历到数组开始节点。该算法的时间复杂度为O(N)。

A = [1,-2,3,5,-3,2]
n, maximum =len(A), -2**31
All = [0]*n # All[i]表示A[i],...,A[n-1]的最大一段子数组
Start = [0]*n # Start[i]表示包含A[i]的最大一段子数组和只有A[i]一个数的最大字段和
# 初始化n
All[-1],Start[-1] = A[-1], A[-1]
for i in range(n-2,-1,-1):
    Start[i] = max(A[i], A[i]+Start[i+1])
    All[i] = max(Start[i],All[i+1])
print(All[0])

优化空间:因为Start,All都的值都只与前一个状态有关,因此可以用变量来表示。

A = [1,-2,3,5,-3,2]
n, maximum =len(A), -2**31
# 初始化n
All,Start = A[-1], A[-1]
for i in range(n-2,-1,-1):
    Start = max(A[i], A[i]+Start)
    All = max(Start,All)
print(All)

换一种写法:

A = [1,-2,3,5,-3,2]
n, maximum =len(A), -2**31
# 初始化n
All,Start = A[-1], A[-1]
for i in range(n-2,-1,-1):
    if Start < 0:
        Start = 0
    Start += A[i]
    if Start > All:
        All = Start
print(All)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值