连续子序列最大和问题—python实现
问题:连续子序列最大和
给定一个数字序列[A1A2A3…An],求i,j(1<=i<=j<=n)使得Ai…Aj和最大,
输出这个最大和(连续大子序列最大和)
例如:
输入:
L=[-2 ,6, -1, 5, 4, -7, 2, 3]
输入:
14 (即6,-1,5,4这个序列的和)
方法一
暴力算法 时间复杂度O(N^3)
即求出每个子序列的和,找到最大的和
def maxsub1(nums):
maxsub = nums[0]
for i in range(len(nums)):#i为子序列起始位置
j = i
for j in range(len(nums)):#j为子序列的结束位置
s = 0
k = j
for k in range(i,j+1):#遍历子序列求和
s += nums[k]
if s > maxsub:
maxsub = s
return maxsub
方法二
简单优化 时间复杂度O(N^2)
按照上面的算法,计算子序列[Ai…A[j]]的和,我们需要遍历
子序列的每个元素,其实我们在计算[Ai…A[j]的和之前,已经
计算了[Ai…A[j-1]]的和,因此只需计算sum(A[i]…A[j-1]]+A[j]
即可得到子序列[Ai…A[j]]的和
def maxsub2(nums):
maxsub = nums[0]
for i in range(len(nums)):#i为子序列起始位置
j = i
s = nums[i]
for j in range(len(nums)):#j为子序列的结束位置
s += nums[k]
if s > maxsub:
maxsub = s
return maxsub
方法三
分治算法 时间复杂度O(NlogN)
将序列平均分为左右两个子序列,存在下面三种情况:
1.要求的连续子序列在左序列中
2.要求的连续子序列在右序列中
3.要求的连续子序列刚好横跨分割点,即左右序列各占一部分
对于第1、2种情况:分别利用递归求出连续子序列的最大和S1、S2,
对于第3情况:最大子序列和由左序列最大和(包含左序列最后一个元素)
右序列最大和(包含右序列第一个元素)构成,
将两个和相加得到S3
那么要求的最大子序列和为这三个数S1,S2,S3的最大者
例如:
左序列 | 右序列
4 -3 5 -2 | -1 2 6 -2
左序列最大子序列和为6(A1到A3)
右序列最大子序列和为8(A6到A7)
横跨分割点的最大子序列的和为11:
即左序列包含最后一个元素最大和4(A1到A4),右序列包含第一个元素最大和7(A5到A7)
4+7=11
方法四
动态规划 时间复杂度O(N)
分析
步骤1:
令dp[i]表示已A[i]作为结尾的连续子序列的最大和
步骤2:
因为dp[i]要求必须以A[i]结尾的连续序列,那么只有两种情况:
1.这个最大连续序列只有一个元素,即以A[i]开始,以A[i]结尾
2.这个最大和的连续序列有多个元素,即以A[p]开始(p小于i),以A[i]结尾
对于情况1,最大和就是A[i]本身
对于情况2,最大和是da[i-1]+A[i]
于是得到状态转移方程:
dp[i]=max{A[i],dp[i-1]+A[i]}
步骤3:
连续子序列的和为
maxsub[n]=max{dp[i]} (1<=i<=n)
def maxsum4(nums):
if len(nums) == 1:
return nums[0]
dp = res = nums[0]
for i in range(1,len(nums)):
dp = max(nums[i],dp + nums[i])
res = max(dp,res)
return res
下边是测试代码
from random import randrange
import time
#方法一
def maxsum1(nums):
maxsum = nums[0]
print(len(nums))
for i in range(len(nums)):#i为子序列起始位置
j = i
for j in range(i,len(nums)):#j为子序列的结束位置
s = 0
k = j
for k in range(i,j+1):#遍历子序列求和
s += nums[k]
if s > maxsum:
maxsum = s
return maxsum
#方法二
def maxsum2(nums):
maxsum = nums[0]
#print(len(nums))
for i in range(len(nums)):#i为子序列起始位置
j = i
s = 0
for j in range(i,len(nums)):#j为子序列的结束位置
s += nums[j]
#print(s)
if s > maxsum:
maxsum = s
return maxsum
#方法三
def maxsum3(nums):
if len(nums) == 1:
return nums[0]
#分组
center = len(nums)//2
left_nums = nums[0:center]
right_nums = nums[center:len(nums)]
#分别求左右序列最大子序列和
left_maxsum = maxsum3(left_nums)
right_maxsum = maxsum3(right_nums)
#求左序列最大和(包括最后一个元素)
left_sum = 0
left_max= left_nums[len(left_nums)-1]
i = len(left_nums)-1
while i >= 0:
left_sum += left_nums[i]
if left_sum > left_max:
left_max = left_sum
i -= 1
#求右序列最大和(包括第一个元素)
right_sum =0
right_max = right_nums[0]
i = 0
while i < len(right_nums):
right_sum += right_nums[i]
if right_sum > right_max:
right_max = right_sum
i += 1
l = [left_maxsum,right_maxsum,left_max + right_max]
return max(l)
#方法四
def maxsum4(nums):
if len(nums) == 1:
return nums[0]
dp = res = nums[0]
for i in range(1,len(nums)):
dp = max(nums[i],dp + nums[i])
res = max(dp,res)
return res
#产生从start到stop的lenth个数组成的队列
def make_list(start,stop,lenth):
nums = []
while lenth:
nums.append(randrange(start,stop,1))
lenth -= 1
return nums
nums = make_list(-10,10,1000)
#L = [-2,6,-1,5,4,-7,2,3]
#print("数组序列:",nums)
s1 = time.time()
value = maxsum1(nums)
s2 = time.time()
print("方法一:",s2-s1)
print("最大连续子序列和:",value)
s1 = time.time()
value = maxsum2(nums)
s2 = time.time()
print("方法二:",s2-s1)
print("最大连续子序列和:",value)
s1 = time.time()
value = maxsum3(nums)
s2 = time.time()
print("方法三:",s2-s1)
print("最大连续子序列和:",value)
s1 = time.time()
value = maxsum4(nums)
s2 = time.time()
print("方法四:",s2-s1)
print("最大连续子序列和:",value)