简介
问题描述
给定一个列表,求该列表中连续最大子序列,以及子序列的最大值。要求时间复杂度为O(n)。
如果想看时间复杂度更多种的解法,可参考:连续子数组的最大和问题的五种解法,语言为C++。
示例
例如:
输入的数组为:
A=[0,-6,3,5,-1,2]。
输出应为:
最大的子数组 [3,5,-1,2];
最大子数组的和 9。
方法一:动态规划
分析
参考网址:动态规划——最大连续子序列和
程序
nums = [9, 11, 1,19, -9, 10, -3, -100, 7, 4,15, -100, -9, 9, 11]
dp = nums.copy()
index_dp = [0]*len(nums)
dp[0] = nums[0]
seq = []
for i in range(1, len(nums)):
A_now = nums[i]
dp[i] = max(A_now, A_now + dp[i-1])
if dp[i-1] < 0:
index_dp[i] = i
else:
index_dp[i] = index_dp[i-1]
Sum_max = max(dp) # 最大和
seq_max = dp.index(Sum_max) # 最大和所在的位置
seq_sum_max = nums[index_dp[seq_max]:seq_max +1] # 最大和的子序列
print('最大子序列为:',seq_sum_max)
print('最大子序列和为:',Sum_max)
方法二
分析
参考网址:求数组中和最大的子数组并输出该子数组序列
需要注意的是,上述网址中的程序有问题,例如,如果输入为: [9, 11, 1,19, -9, 10, -3, -100, 7, 4,15, -100, -9, 9, 11],那么输出的最大连续子序列结果不对或者报错。原因在于,只保留了最大连续子序列的和值,而没有保存对应的序列值。
改进:增加字典,在每一次遇到和值为负数时,对列表进行截断,并且将截断的每一节的数据的最大值和对应序列都保存到字典中。
程序
# 数据读取和处理
nums = [9, 11, 1,19, -9, 10, -3, -100, 7, 4,15, -100, -9, 9, 11]
n = len(nums)
Sum = MaxSum =0
d = []
# 保存最大值以及最大值的连续序列
k = 0 # 索引
temp = {} # 字典
for j in range(0,n):
Sum += nums[j]
if (Sum > MaxSum):
# 动态规划
MaxSum = Sum
d.append(j)
temp[k] = [d,MaxSum]
elif (Sum < 0):
# 截断操作:将遇到负数前所有的最大值都保存
temp[k] = [d,MaxSum]
k = k+1
# 归零
Sum = 0
MaxSum = 0
d = []
# 读取最大连续子序列和最大值
max_value = 0
for i in list(temp.keys()):
value_max = temp[i][1]
if value_max > max_value:
max_value = value_max
seq_max = temp[i][0]
# 输出最大连续子序列和最大值
print('最大子序列为:'+ str(nums[seq_max[0]:seq_max[-1]+1]))
print('最大子序列和为:'+ str(max_value))