最大连续子数组和及子数组
问题描述: 给定一个不为空的数组,数组中一个元素或多个连续元素和称为连续子数组和,找出给定数组中连续子数组和的最大值。
思路: 本题的思路有很多种,本文探讨三种最常用的解法,时间复杂度也各不相同。前两种通过暴力搜索的方式实现,时间复杂度为 O ( N 3 ) O(N^3) O(N3)和 O ( N 2 ) O(N^2) O(N2),效率较为低下;第三种方法采用的是“加入&抛弃”思想,仅需要遍历一次数组即可计算最大子数组和,时间复杂度为 O ( N ) O(N) O(N)。下面我们逐一讲解。
一、三重循环暴力搜索
三重循环的暴力搜索是本题最基本的搜索方法,我们首先需要设置起始下标、结束下标来定位数组中的子数组,添加另一个for循环来遍历子数组以求和,其时间复杂度为
O
(
N
3
)
O(N^3)
O(N3),空间复杂度为
O
(
1
)
O(1)
O(1)。代码如下:
def tribfsearch(self, array):
array = array
if len(array) == 0: return
maxsum = array[0]
sum = 0
maxarray = [array[0]]
for i in range(len(array)):
for j in range(i, len(array)):
for k in range(i, j+1):
sum += array[k]
if sum > maxsum :
maxsum = sum
maxarray = array[i:j+1]
sum = 0
print(maxsum, 'Max Subarray:', maxarray)
return
二、二重循环暴力搜索
二重循环的暴力搜索较于三重循环效率有一定的提升,不同之处在于二重循环是在遍历起始、结束下标的同时进行求和计算,因此不需要再添加for循环来求和。代码如下:
def bibfsearch(self, array):
array = array
if len(array) == 0: return
maxarray = [array[0]]
maxsum = array[0]
sum = 0
for i in range(len(array)):
for j in range(i, len(array)):
sum += array[j]
if sum > maxsum :
maxsum = sum
maxarray = array[i:j+1]
sum = 0
print(maxsum,'Max Subarray:', maxarray)
return
三、“加入&抛弃”方法
使用“加入&抛弃”方法则只用一次循环即可完成最大子数组和的计算。该方法的思路是:设置一个局部子数组和的变量,首先设数组的第一个元素为局部和,在迭代的过程中,将当前元素同局部和求和,如果结果小于或等于0,则将当前局部和的结果抛弃,从下一个元素开始计算和;大于0则继续累加。找出局部和的最大值即可。公式和代码实现如下:
def compact(self, array):
array = array
if len(array) == 0: return
maxsum = array[0]
begin = 0
end = begin + 1
left = 0
sum = array[0]
for i in range(1, len(array)):
if sum <= 0:
sum = array[i]
left = i
right = i + 1
else:
sum += array[i]
right = i+1
if maxsum < sum :
maxsum = sum
begin = left
end = right
maxarray = array[begin:end]
print(maxsum,'Max Subarray:', maxarray)
return
上述代码的最终输出结果均如下所示,所有方法均输出最大子序列和以及子序列:
该方法归类为动态规划方法,但个人认为该方法的动态规划思想并不明显,因为动态规划是先求出子问题,然后求解原问题时参考子问题的结果来做出决策。而该方法虽然考虑了子问题,但子问题是dp[i-1],判断准则是其是否大于0. 因此个人认为该方法类似于“加入、抛弃”操作,不断加入新元素,并根据局部和是否为正数判断其能否构成最大子序列的一部分,能则继续添加新元素,不能则重新计算。