求解最大子数组问题
最大子数组问题就是寻找数组A的和最大的非空连续子数组。当然数组A必须包含正负数,如果全是负数或正数则没有意义了,最大子数组显然是数组A本身。
当然我们可以设计一个暴力方法来求解,穷举所以可能的情况。但是这里我们用算法学习2中学过的分治法来求解。但是这个跟归并排序有点不一样,归并排序求解的子问题结果可以直接处理得到原问题的结果,这里并不能通过处理分解后的子问题的解来得到原问题的答案。图a,b很好的说明了原因。
我们在A[low,mid]中通过FIND_MAX_SUBARRAY得到最大子问题不一定是A[low,high]的最大子数组,同理在A[mid+1,high]中也一样。但是A[low,high]的最大子数组必然是A[low,mid]的最大子数组,A[mid+1,high]的最大子数组和跨越mid的最大子数组三个中的最大者。如果能求出跨越mid的最大子数组,问题就解决了。
FIND_MAX_CROSSING_SUBARRAY(A,low,high)
if high > low
mid = (low + high)/2
i = 0 //最大子数组的左下标
Lmax = 0 //跨越mid左边部分最大子数组的和
j = 0 //最大子数组的右下标
Rmax = 0 //跨越mid右边部分最大子数组的和
k = mid
sum = 0
while k >= low
sum = sum + A[k]
if sum > Lmax
Lmax = sum
i = k
k = k - 1
k = mid + 1
sum = 0
while k <= high
sum = sum + A[k]
if sum > Rmax
Rmax = sum
j = k
k = k - 1
return (i,j,Lmax + Rmax)
结合分解求解,找出最大一组解
//假设函数FIND_MAX_SUBARRAY是一个已经正确实现了查找最大子数组问题这一点对于我们理解把问题拆分求解很有帮助
//返回子数组边界的下标i<=j和sum,(i,j,sum)
FIND_MAX_SUBARRAY(A,low,high)
if low < high
mid = low + high/2
(Li,Lj,Lsum) = FIND_MAX_SUBARRAY(A,low,mid)
(Ri,Rj,Rsum) = FIND_MAX_SUBARRAY(A,mid+1,high)
(Ci,Cj,Csum) = FIND_MAX_CROSSING_SUBARRAY(A,low,high)
//找出最大的和,返回相应的结果
if max(Lsum,Rsum,Csum) == Lsum
return (Li,Lj,Lsum)
else if max(Lsum,Rsum,Csum) == Rsum
return (Ri,Rj,Rsum)
else
return (Ci,Cj,Csum)
下面贴出书本上给出的伪代码,除了表达上有些差别外,逻辑上是一样的
FIND_MAX_CROSSING_SUBARRAY(A,low,mid,high)
left-sum = -∞
sum = 0
for i=mid downto low
sum = sum + A[i]
if left-sum < sum
left-sum = sum
max-left = i
right-sum = -∞
sum = 0
for j=mid+1 to high
sum = sum + A[j]
if right-sum < sum
right-sum = sum
max-right = j
return (max-left,max-right,left-sum + right-sum)
FIND_MAX_SUBARRAY(A,low,high)
if low == high //数组只有一个元素时,不可拆分,当然结果也是显然的
return (low,high,A[low])
else
mid = (low + high)/2 //结果取整数部分
(left-low,left-high,left-sum)=FIND_MAX_SUBARRAY(A,low,mid)
(right-low,right-high,right-sum)=FIND_MAX_SUBARRAY(A,mid+1,high)
(cross-low,cross-high,cross-sum)=FIND_CROSS_MAX_SUBARRAY(A,low,mid,high)
if left-sum ≥ right-sum and left-sum ≥ cross-sum
return (left-low,left-high,left-sum)
elif right-sum ≥ left-sum and right-sum ≥ cross-sum
return (right-low,right-high,right-sum)
else
return (cross-low,cross-high,cross-sum)