1 采用分治法
思想
将数组划分为左右两部分,查找左半部分与右半部分的最大子数组 与 当前问题类似,采用递归即可。
主要问题集中如何解决“最大子数组出现在跨域左右两部分”的时候,此时仅需从中间位置向两边查找最大和的边界位置即可。
时间复杂度: O(nlogn)
实现代码
1 | def find_maximum_subarray(array, low=0, high=None): |
2 采用非递归、线性时间的算法
思想
出自《算法导论》4.1-5练习题:
使用如下思想为最大子数组问题设计一个非递归的、线性时间的算法。从数组的左边界开始,从左至右处理,记录到目前为止已经处理过的最大子数组。若已知A[1..j]的最大子数组,基于如下性质将解扩展为A[1..j+1]的最大子数组:A[1..j+1]的最大子数组要么是A[1..j]的最大子数组,要么是某个子数组A[i..j+1] (1≤i≤j+1)。在已知A[1..j]的最大子数组的情况下,可以在线性时间内找出形如A[i..j+1]的最大子数组。
划重点: A[1..j+1]的最大子数组要么是A[1..j]的最大子数组,要么是某个子数组A[i..j+1] (1≤i≤j+1)
在将数组由左向右处理时,A[1..j]的最大子数组是已求解,即已知的,需要求解的就是子数组A[i..j+1] (1≤i≤j+1)。此处对于数组A[i..j+1]要查找的最大解是包含边界A[j+1]的,否则就落入了对立条件A[1..j]数组中了。
对于寻找包含边界A[j+1]在内的最大解的数组A[i..j+1]而言,最大解要么仅仅就是元素A[j+1],要么就是A[j+1]及其前面元素一起。而对于A[j+1]及其前面元素一起的情况,其中隐含的条件是一定包含了以A[j] (A[j+1]的前一个元素)为边界的最大解,换言之,此种情况就是A[j+1]+(A[i..j]的最大解)。
算法由左向右处理数组时,需要记录的值有:
- A[1..j]的最大子数组及其和 (注:此处的最大子数组不一定包含边界A[j],可能位于1与j的开区间内)
- A[i..j] (1≤i≤j)的最大子数组及其和 (注:此处的最大子数组是一定包含边界A[j]在内的,即以A[j]为起始向左前寻找最大的解,不同于上一个值)
比较过程:
- 比较A[i..j] (即包含边界A[j]) + A[j+1] 与A[j+1]的大小,大者为A[i..j+1]的最大解
- 比较A[1..j]与A[i..j+1]的最大解,大者为A[1..j+1]的最大解
时间复杂度:O(n)
实现代码
1 | def find_maximum_subarray_linear(array): |