最大连续子数组:从暴力到分治
Views: --
给定数组 ,寻找非空连续区间 ,使
最大。这是最大连续子数组问题,也常叫最大子段和。
1. 从股票价格到差分数组
若已知每天价格 ,在第 天买、第 天卖的收益是 。令
则
因此“买卖一次获得最大收益”可以转成差分数组的最大连续子数组。
2. 三种直接方法
2.1 枚举区间并逐段求和
有 个区间,每个区间再花 求和,总计 。
2.2 前缀和
定义
则区间和为 。每个区间可 计算,总计 。
2.3 维护最小前缀
对每个右端点 ,最佳左端点对应此前最小的 。单次扫描即可做到 。这与后面动态规划版 Kadane 算法本质相同。
本讲重点先用它理解分治。
3. 分治的三种位置
取中点 。最优区间只有三种可能:
- 完全位于左半区;
- 完全位于右半区;
- 跨过中点。
前两种递归解决。跨中点区间一定由“以 结尾的最大后缀”和“以 开始的最大前缀”组成。
4. 如何在线性时间求跨中点答案
bestLeft = -infinity
sum = 0
for i = m down to l:
sum += A[i]
bestLeft = max(bestLeft, sum)
bestRight = -infinity
sum = 0
for j = m + 1 to r:
sum += A[j]
bestRight = max(bestRight, sum)
cross = bestLeft + bestRight
两次扫描合计 。
5. 完整算法
MaxSubarray(A, l, r):
if l == r:
return A[l]
m = floor((l + r) / 2)
left = MaxSubarray(A, l, m)
right = MaxSubarray(A, m + 1, r)
cross = MaxCrossing(A, l, m, r)
return max(left, right, cross)
若还要返回区间端点,就让每个返回值同时携带 。
6. 正确性
任意连续区间相对中点只能属于上述三类,不存在第四种情况。递归分别求出左右半区的最优解;跨中点时,左侧若不是最大后缀,替换成更大的后缀会得到更优区间,矛盾,右侧同理。因此三者取最大就是全局最优。
7. 复杂度
由主定理得
辅助数组不需要,递归栈为 。
8. 全负数的边界
若要求子数组非空,全负数组的答案应是最大单个元素。初始化为 0 会错误地返回空数组。
例如 :
- 非空定义:答案是 ;
- 允许空数组:答案可以是 0。
题目必须先明确采用哪种定义。课件和多数算法教材采用非空子数组。
9. 一个完整例子
对
最优区间是
和为 43。分治法会在某一层把它识别为跨中点区间。
10. 为什么还要学 解法
最大子数组确实存在 算法,但分治版展示了一个可迁移思路:
- 先枚举最优解相对分界点的位置类型;
- 递归处理完全落在子问题中的情况;
- 为跨边界情况设计高效合并。
逆序对、最近点对、平面分治等问题都会重复这个结构。