问题描述
给定一个含有n个元素的数组,要求找到一个起始位置和一个结束位置,使这两个元素之间的元素之和最大,
数组示例:[13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
如下图:
即选取局部连续元素之和最大的的子数组。
第一种解法
将数组从中间切开,那么最大子数组可能存在三种情况:
- 最大子数组在左半部分
- 最大子数组在右半部分
- 最大子数组横跨左右两部分
代码实现:
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))
结果: