分治策略
最大子数组问题
问题描述:
给定一个数组,求出他的最大子数组,即此子数组的和最大。
分治求解:
数组A[low,high],mid=(low+high)/2
- 最大子数组存在的三种情况:
a. 最大子数组在[low,mid]中,
b. 最大子数组在[mid+1,high]中,
c. 最大子数组跨越了mid, [i…mid…j]中 - 假如是前两种情况,那么最大子数组可以继续分解
- 只有当分解到横跨中点的子数组时,才去求解这个子数组的和。
- 因此函数FIND-MAX-CROSSING-SUBARRARY(A, low,mid, high), 是对横跨中点的子数组求和,返回该和以及下标元组。
伪代码:
- 分治算法的伪代码:
分析:
第一行测试基本情况,第二行返回下标和元素值。当只有一个元素时,他就是最大的。
第三行划分数组,计算mid
4-5行,分别递归求解左右子数组中的最大子数组。
6-11行完成合并工作。
分治算法分析
FIND-MAXIMUM-SUBARRAY
n=1基本情况,1-2行,花费常量
T(1) = θ(1)
n>1另n为2的幂
4-5行,两个递归过程:2T(n/2)
6调用FIND-MAX-CROSSING-SUBARRAY
花费Θ(n).
7-11花费为θ(1)
因此:T(n) = θ(1)+2T(n/2)+Θ(n)+θ(1)=2T(n/2)+Θ(n)
根据前面对T(n/2)的推导,时间复杂度为Θ(nlgn)。
编程实现
import sys
# index = 0
def maxcrossarray(A,p,mid,r):
sum = 0
maxleft = 0
leftsum = -float("inf")
for i in range(mid,p-1,-1):
sum = sum+A[i]
if sum > leftsum:
leftsum = sum
maxleft = i
sum = 0
rightsum = -float("inf")
maxright = 0
for j in range(mid+1, r+1):
sum = sum + A[j]
if sum > rightsum:
rightsum = sum
maxright = j
return(maxleft,maxright,leftsum+rightsum)
def maxsubarray(A,p,r):
if p==r:
return (p,r,A[p])
else:
mid = int((p+r)/2)
(leftlow,lefthigh,leftsum) = maxsubarray(A,p,mid)
(rightlow, righthigh,rightsum) = maxsubarray(A,mid+1,r)
(crosslow, crosshigh,crosssum) = maxcrossarray(A,p,mid,r)
if leftsum>=rightsum and leftsum>=crosssum:
return(leftlow, lefthigh,leftsum)
elif rightsum>=leftsum and rightsum>=crosssum:
return(rightlow, righthigh, rightsum)
else:
return (crosslow, crosshigh, crosssum)
if __name__ == "__main__":
A = list(map(int, sys.stdin.readline().strip().split(' ')))
index = maxsubarray(A, 0, len(A)-1)
print(index)
当输入序列A的元素全为负时,输出为最大负数的下标和值
暴力解法:
import sys
# index = 0
def violance(A,p,r):
maxsum = -float("inf")
for i in range(p,r):
sum =0
for j in range(i,r):
sum = sum + A[j]
if sum>maxsum:
maxsum = sum
maxleft = i
maxright = j
return (maxleft,maxright,maxsum)
if __name__ == "__main__":
A = list(map(int, sys.stdin.readline().strip().split(' ')))
index = violance(A, 0, len(A))
print(index)
线性解法:
对数组由左至右处理,记录已经处理的最大子数组。若已知A[1,j]的最大子数组,A[1,j+1]的最大子数组要么是某个子数组Ai,j+1(1≤i≤j+1),要么是A[1,j]的最大子数组。在已知A[1,j]最大子数组的情况下,可以在线性时间内找出A[1,j+1]的最大子数组。
思想: 当最大子数组A[i,j]的和sum>0,那么A[i,j+1]就是A[1,j+1]的最大子数组,反之,则重新开始计算A[j+1]就是A[1,j+1]的最大子数组。
import sys
# index = 0
def lineartime(A,p,r):
maxsum = -float("inf")
cursum = -float("inf")
for j in range(p,r):
if cursum>0:
cursum = cursum+A[j]
else:
cursum = A[j]
curleft = j
if cursum>maxsum:
maxsum = cursum
maxleft = curleft
maxright = j
return (maxleft,maxright,maxsum)
if __name__ == "__main__":
A = list(map(int, sys.stdin.readline().strip().split(' ')))
index = lineartime(A, 0, len(A))
print(index)