给定以数组,找出连续子序列的最大和
例如,
nums = [3, -4, 2, -1, 2, 6, -5, 4]
其中,最大的子序列为 [2,−1,2,6][2, -1, 2, 6][2,−1,2,6],结果为 999。
1 枚举
1.1 直接枚举
枚举法可以分成两个部分:
① 计算位置iii到位置jjj这段连续子序列的和,记为SCS[i][j]SCS[i][j]SCS[i][j]
② 双指针循环遍历iii和jjj
具体代码如下:
def SCS(nums, i, j):
scs = 0
for t in range(i, j + 1):
scs += nums[t]
return scs
def max_of_SCS(nums):
n = len(nums)
max_len = 0
for i in range(n):
for j in range(i, n):
max_len = max(max_len, SCS(nums, i, j))
return max_len
nums = [3, -4, 2, -1, 2, 6, -5, 4]
print(max_of_SCS(nums))
算法时间复杂度:O(n3)O(n^3)O(n3)
1.2 改进
O(n3)O(n^3)O(n3)的时间复杂度太高了,通过观察可以发现,在最内层循环的时候,并不需要分别去计算每个SCS[i][j]SCS[i][j]SCS[i][j]
而是在遍历jjj的时候将从iii到jjj的最大SCSSCSSCS保存下来即可
修改代码如下:
def max_of_SCS(nums):
n = len(nums)
max_Sum = 0
for i in range(n):
this_Sum = 0
for j in range(i, n):
this_Sum += nums[j]
max_Sum = max(max_Sum, this_Sum)
return max_Sum
nums = [3, -4, 2, -1, 2, 6, -5, 4]
print(max_of_SCS(nums))
算法时间复杂度:O(n2)O(n^2)O(n2)
2 动态规划
2.1 最优性条件
假设存在一个最大和连续子序列CS[i][j]CS[i][j]CS[i][j],则以i−1i-1i−1为结尾的连续子序列CS[∗][i−1]CS[*][i-1]CS[∗][i−1],和以j+1j+1j+1为开头的连续子序列CS[j+1][∗]CS[j+1][*]CS[j+1][∗]的连续子序列的最大和一定0。
即,
CS[∗][i−1]≤0,∀∗∈[0,i−2]CS[*][i-1] \le 0, \forall * \in [0, i-2]CS[∗][i−1]≤0,∀∗∈[0,i−2]
CS[j+1][∗]≤0,∀∗∈[j+2,n−1]CS[j+1][*] \le 0, \forall * \in [j+2, n-1]CS[j+1][∗]≤0,∀∗∈[j+2,n−1]
2.2 最优性条件证明
反证法:
假设存在一个最大和连续子序列CS[i][j]CS[i][j]CS[i][j],并且存在CS[t][i−1]>0,t∈[0,i−2]CS[t][i-1] > 0, t \in [0,i-2]CS[t][i−1]>0,t∈[0,i−2]
则CS[t][j]CS[t][j]CS[t][j]=CS[t][i−1]+CS[i][j]>CS[i][j]CS[t][i-1]+CS[i][j]>CS[i][j]CS[t][i−1]+CS[i][j]>CS[i][j]
与CS[i][j]CS[i][j]CS[i][j]是最大值矛盾
2.2 状态更新
将从000到iii中的连续子序列的最大和记为MSCS[i]MSCS[i]MSCS[i]
MSCS[0]MSCS[0]MSCS[0]=num[0]num[0]num[0]
状态更新函数可以定义为:
① 如果MSCS[i−1]>0MSCS[i-1]>0MSCS[i−1]>0
MSCS[i]MSCS[i]MSCS[i]=MSCS[i−1]+num[i]MSCS[i-1]+num[i]MSCS[i−1]+num[i]
② 如果MSCS[i−1]≤0MSCS[i-1] \le 0MSCS[i−1]≤0
MSCS[i]MSCS[i]MSCS[i]=0+num[i]0+num[i]0+num[i]
2.3 代码
def max_of_SCS(nums):
n = len(nums)
MSCS = [0] * n
MSCS[0] = nums[0]
for i in range(1, n):
if MSCS[i-1] > 0:
MSCS[i] = MSCS[i-1]+nums[i]
else:
MSCS[i] = nums[i]
return max(MSCS)
nums = [3, -4, 2, -1, 2, 6, -5, 4]
print(max_of_SCS(nums))
算法时间复杂度:O(n)O(n)O(n)
3 二分法
3.1 二分规则
假设将数组nums0,n−1nums_{0,n-1}nums0,n−1二分为nums0,mnums_{0,m}nums0,m和numsm+1,n−1nums_{m+1,n-1}numsm+1,n−1,则连续子序列的最大和有三种情况:
- 左子数组的连续子序列的最大和,记为MSCS[0][m]MSCS[0][m]MSCS[0][m]
- 右子数组的连续子序列的最大和,记为MSCS[m+1][n−1]MSCS[m+1][n-1]MSCS[m+1][n−1]
- 左子数组中以mmm为结尾的连续子序列的最大和+++右子数组中以m+1m+1m+1为开头的连续子序列的最大和,记为MSCS[i][m]+MSCS[m+1][j],i∈[0,m−1],j∈[m+2,n−1]MSCS[i][m]+MSCS[m+1][j],i \in [0,m-1],j \in [m+2,n-1]MSCS[i][m]+MSCS[m+1][j],i∈[0,m−1],j∈[m+2,n−1]
3.2 思路
函数MSCS(nums,i,j)MSCS(nums, i, j)MSCS(nums,i,j)
如果i==ji==ji==j,返回nums[i]nums[i]nums[i];
否则,按照返回上面的三种情况的最大值。
3.3 代码
def MSCS(nums, i, j):
if i == j:
return nums[i]
m = (i+j) // 2
max_l = MSCS(nums, i, m)
max_r = MSCS(nums, m+1, j)
max_mix_l = 0
mix_l = 0
for t in reversed(range(i, m+1)):
mix_l += nums[t]
max_mix_l = max(max_mix_l, mix_l)
max_mix_r = 0
mix_r = 0
for t in range(m+1, j+1):
mix_r += nums[t]
max_mix_r = max(max_mix_r, mix_r)
return max(max_r, max_l, max_mix_l+max_mix_r)
def max_of_SCS(nums):
return MSCS(nums, 0, len(nums)-1)
nums = [3, -4, 2, -1, 2, 6, -5, 4]
print(max_of_SCS(nums))
算法时间复杂度:O(nlog2n)O(nlog_2n)O(nlog2n)
4 对比
算法 | 时间复杂度 | n=8000时,耗时 / s |
---|---|---|
枚举法 | O(n2)O(n^2)O(n2) | 2.778 |
动态规划 | O(n)O(n)O(n) | 0.001 |
二分法 | O(nlog2n)O(nlog_2n)O(nlog2n) | 0.015 |