求取最大子数组(Python语言实现)

问题描述

给定一个含有n个元素的数组,要求找到一个起始位置和一个结束位置,使这两个元素之间的元素之和最大,

数组示例:[13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]

如下图:

在这里插入图片描述
即选取局部连续元素之和最大的的子数组。

第一种解法

将数组从中间切开,那么最大子数组可能存在三种情况:

  1. 最大子数组在左半部分
  2. 最大子数组在右半部分
  3. 最大子数组横跨左右两部分

代码实现:


import sys

def  find_max_crossing_subarray(array, mid):
    '''
    查找横跨作用两部分的最大子数组
    '''

    '''
    sys.maxsize是Python平台的指针那规定Python中列表和字符串的最大大小。 
    maxsize返回的大小值取决于计算机系统:
    32位:该值将为2^31-1,即2147483647
    64位:该值将为2^63-1,即9223372036854775807
    '''
    max_sum = -sys.maxsize - 1
    sum_now = 0
    i = mid
    left = i
    while i >= 0:  #查找包含left_end的左边子数组
        sum_now += array[i]
        if sum_now > max_sum:
            left = i  #记录下元素和最大时的元素下标
            max_sum = sum_now
        i -= 1
    left_max = max_sum
    j = mid + 1
    sum_now = 0
    right = j
    max_sum = -sys.maxsize - 1
    while j < len(array):  #查找包含right_begin的右边子数组
        sum_now += array[j]
        if sum_now > max_sum:
            right = j
            max_sum = sum_now
        j += 1
    right_max = max_sum
    return array[left:right+1], left_max + right_max

def  find_max_subarray(array):
    if len(array) <= 1:
        return array, array[0]  #当数组元素个数小于等于1时,直接返回
    left_part = array[0: int(len(array)/2)]  #将数组分割成两部分
    right_part = array[int(len(array)/2):len(array)]
    left_sub_array, left_max = find_max_subarray(left_part)  #递归求取左半部分最大子数组
    right_sub_array, right_max = find_max_subarray(right_part) #递归求取有半部分最大子数组
    crossing_sub_array, crossing_max = find_max_crossing_subarray(array, int(len(array)/2) - 1) #获得横跨左右两部分的最大子数组
    max_sub_array, max_sum = left_sub_array, left_max
    if right_max > left_max:
        max_sub_array, max_sum = right_sub_array, right_max
    if crossing_max > max_sum:
        max_sub_array, max_sum = crossing_sub_array, crossing_max
    return max_sub_array, max_sum  #三种情况中,元素和最大的数组就是整个数组的最大子数组
    
array = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
sub_array, max_sum = find_max_subarray(array)
print("最大子数组是:{0} ,最大子数组之和为: {1}".format(sub_array, max_sum))

结果:
在这里插入图片描述

设该算法时间复杂度为T(n),T(n) = T(n/2) + T(n/2) + O(n)

可得该算法的时间复杂度为:n*lg(n)

第二种解法

我们还可以找到时间复杂度更好的算法,用[i,j]表示一段子数组,如果[i,j]对应最大子数组,那么必有sum([i,k])>=0 , k=i,i+1,i+2 …,j。
根据这个特性就可以设计更好的查找算法。
代码实现:


import sys
def linear_find_max_subarray(array):
    '''
    线性时间复杂度查找最大子数组
    '''
    left = 0
    right = 0
    i = left
    j = right
    max_sum = -sys.maxsize - 1
    sum_now = 0
    while j < len(array):
        sum_now += array[j]
        if sum_now > max_sum:  # 元素和增大,调整相关变量记录当前元素构成的子数组
            max_sum = sum_now
            left = i
            right = j
        if sum_now < 0:  # 如果元素和小于0,那么[i:j]就不属于最大子数组的一部分
            sum_now = 0
            i = j + 1
        j += 1
    return array[left:right + 1], max_sum


array = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
max_subarray, max_sum = linear_find_max_subarray(array)
print("最大子数组是:{0} ,最大子数组之和为: {1}".format(max_subarray, max_sum))

结果:
在这里插入图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰履踏青云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值