问题描述: 一个N个整数的数组(A[0] ... A[n-1]), 求这个数组的子数组的最大和。
举例:
数组:A = [1, -2, 3, 5, -3, 2] 返回: 8
解法一: 穷举法
穷举出所有的子数组,分别求和,最后选出最大值
n个元素的组合有n*(n-1)种,对每种组合求和需要c次,其中c为该组合元素数
因而该算法复杂度为O(n2)*O(n) = O(n3)
假设m[i, j]表示子数组(A[i]... A[j]), 则该子数组的和等于m[i, j-1] + A[j],所以只要
知道m[i, j-1],就可以一次计算出m[i, j] 的值
所以可以改进算法, 通过避免重复计算子数组之和来降低复杂度,改进后T(n) = O(n2)
Python实现:
def SubMax_force(arr): m = {} maxnum = 0 for i inrange(len(arr)): m[(i,i-1)]= 0 for j inrange(i, len(arr)): m[(i,j)] = m[(i,j-1)] + arr[j] if m[(i, j)] > maxnum: maxnum = m[(i, j)] return maxnum
解法二: 分治法
把数组分为A[0]...A[n/2 - 1]和A[n/2]...A[n-1]两部分,分别递归求出其最大子段和
则A[0]...A[n-1]最大子段和可能来自与以下三个子数组之一:
1. (A[0]...A[n/2 - 1])
2. (A[n/2]...A[n-1)
3. (A[i]...A[j]) 其中i,j 跨越左右两个子数组
其中1,2 可以分别递归求解, 而3需要单独求解
对于3 可以分别求解以A[n/2]结尾的最大子段和以A[n/2+1]为首的最大子段,然后
两者相加即可。
该解法算法复杂度O(n*log2n)
Python实现:
def SubMax_divide(arr): if len(arr)== 1: returnarr[0] sum1 =SubMax_divide(arr[0:len(arr)/2]) sum2 =SubMax_divide(arr[len(arr)/2: len(arr)]) sum31 =arr[len(arr)/2-1] sum32 =arr[len(arr)/2] tmp = 0 for i inrange(len(arr)/2)[::-1]: tmp +=arr[i] if tmp> sum31: sum31= tmp tmp = 0 for i inrange(len(arr)/2, len(arr)): tmp +=arr[i] if tmp> sum32: sum32= tmp sum3 =sum31+sum32 returnmax(max(sum1,sum2),sum3)
解法三: 动态规划
动态规划的两个关键因素是“最优子结构”和“重叠子问题”
“最优子结构”指的是一个问题的最优解包含子问题的一个最优解
对于本例,考虑A[i]...A[j]的拥有最大和的子段必定是以下三种情况之一:
1.该子段仅包含A[i]
2.该子段包含且不仅A[i], 则该子段中除去A[i]的部分必然是A[i+1]...A[j]的以A[i+1]为首的拥有最大和的子段
3.该子段不包含A[i],则该子段必然是A[i+1]...A[j]的拥有最大和的子段
以上描述表明该问题拥有最优子结构,且该问题包含两种子问题:
以A[i]为首的最大和子段,记为Start[i]
A[i]...A[j]的最大和子段,记为m[i, j]
该算法复杂度为O(n),使用了两个数组存储中间值
Python实现:
def SubMax_dp(arr): Start = {} m = {} m[len(arr)-1]= Start[len(arr)-1] = arr[len(arr)-1] for i inrange(len(arr)-1)[::-1]: Start[i]= max(arr[i],Start[i+1] + arr[i]) m[i] =max(Start[i], m[i+1]) return m[0]
空间优化:
由于Start[i],m[i]仅依赖于数组的后一个值Start[i+1],m[i+1],且数组从后向前遍历,所以可以把数组改为中间值存储
def SubMax_dp_leSp(arr): m= Start = arr[len(arr)-1] for i in range(len(arr)-1)[::-1]: Start = max(arr[i],Start + arr[i]) m = max(Start, m) print 'i =',i,'Start =',Start,'m =',m return m
编程之美:
一下算法表明了此问题的一个有趣的解法,即从一段向另一端遍历数组,并累计和,若和为负值,则丢弃所有的元素,并从头开始计算,直到最后一个大于零的元素为止
def SubMax_beauty(arr): Start = arr[len(arr)-1] MAX = 0 for i in range(len(arr)-1)[::-1]: if Start < 0: Start = 0 Start += arr[i] if Start > MAX: MAX = Start return MAX