算法复习——分而治之篇之最大子数组问题
以下内容主要参考中国大学MOOC《算法设计与分析》,墙裂推荐希望入门算法的童鞋学习!
1. 问题背景
子数组:数组中连续的一段序列,例如 X [ 3..7 ] X[3..7] X[3..7];
子数组和:子数组中元素的求和, X [ 3..7 ] X[3..7] X[3..7]的和就是 3 + 5 − 4 + 3 + 2 = 9 3+5-4+3+2=9 3+5−4+3+2=9;
那么,问题就是如何寻找数组 X X X中最大的非空子数组?
2. 问题定义
最大子数组问题(Max Continuous Subarray)
输入:
- 给定一个数组 X [ 1.. n ] X[1..n] X[1..n],对于任意一对数组下标为 l , r ( l ≤ r ) l, r(l \leq r) l,r(l≤r)的非空子数组,其和记为 S ( l , r ) = ∑ i = l r X [ i ] S(l, r)=\sum_{i=l}^{r}X[i] S(l,r)=∑i=lrX[i]
输出:
- 求出 S ( l , r ) S(l, r) S(l,r)的最大值,记为 S m a x S_{max} Smax
3. 分而治之
分而治之的一般步骤是:分解原问题、解决子问题、合并问题解。
-
分解原问题:将数组 X [ 1.. n ] X[1..n] X[1..n]分为 X [ 1.. n 2 ] X[1..\frac{n}{2}] X[1..2n]和 X [ n 2 + 1.. n ] X[\frac{n}{2}+1..n] X[2n+1..n];
-
解决子问题:
- S 1 S_{1} S1:数组 X [ 1.. n 2 ] X[1..\frac{n}{2}] X[1..2n]的最大子数组;
- S 2 S_{2} S2:数组 X [ n 2 + 1.. n ] X[\frac{n}{2}+1..n] X[2n+1..n]的最大子数组;
-
合并问题解:得到 S m a x S_{max} Smax
- S 3 S_{3} S3:跨中点的最大子数组。
- 数组 X X X的最大子数组之和 S m a x = m a x { S 1 , S 2 , S 3 } S_{max}=max\{S_{1}, S_{2}, S_{3}\} Smax=max{S1,S2,S3}。
子问题可以通过递归的方式进行求解;那么,问题就聚焦在如何合并问题解,求解 S 3 S_{3} S3?
求解 S 3 S_{3} S3:
分解问题时,我们在 m i d = n 2 mid=\frac{n}{2} mid=2n处将数组分成了左右部分,分别得到了左右部分的最大子数组和,那现在我们要求跨中点的最大子数组,也就说必须该最大子数组必须包含 X [ m i d ] X[mid] X[mid]和 X [ m i d + 1 ] X[mid+1] X[mid+1]。
因此,我们可以用 L e f t Left Left来表示以 X [ m i d ] X[mid] X[mid]为结尾的最大子数组之和,用 R i g h t Right Right来表示以 X [ m i d + 1 ] X[mid+1] X[mid+1]为开头的最大子数组之和,则 S 3 = L e f t + R i g h t S_{3}=Left+Right S3=Left+Right。
求解 L e f t Left Left可以从 X [ m i d ] X[mid] X[mid]向前遍历求和,并记录最大值;求解 R i g h t Right Right可以从 X [ m i d + 1 ] X[mid+1] X[mid+1]向后遍历求和,并记录最大值。因此,求解 S 3 S_{3} S3的时间复杂度是 O ( n ) O(n) O(n)。
4. 伪代码
初始调用 M a x S u b A r r a y ( X , 1 , n ) MaxSubArray(X, 1, n) MaxSubArray(X,1,n)
MaxSubArray(X, low, high)
输入:数组 X X X,数组下标 l o w low low, m i d mid mid
输出:最大子数组之和 S m a x S_{max} Smax
if low = high then
return X[low]
end
else
mid ← (low + high) // 2
S_1 ← MaxSubArray(X, low, mid)
S_2 ← MaxSubArray(X, mid+1, high)
S_3 ← CrossingSubArray(X, low, mid, high)
S_max ← max{S_1, S_2, S_3}
return S_max
end
CrossingSubArray(X, low, mid, high)
输入:数组 X X X,数组下标 l o w low low, m i d mid mid, h i g h high high
输出:跨越中点的最大子数组之和 S 3 S_{3} S3
S_left ← -∞
Sum ← 0
for l ← mid downto low do
Sum ← Sum + X[l]
S_left ← max{S_left, Sum}
end
S_right ← -∞
Sum ← 0
for r ← mid+1 to high do
Sum ← Sum + X[r]
S_right ← max{S_right, Sum}
end
S_3 ← S_left + S_right
return S_3
基于以上的伪代码,可以进行时间复杂度分析,得到
T
(
n
)
T(n)
T(n)的递归式。
T
(
n
)
=
{
1
,
n
=
1
2
T
(
n
2
)
+
O
(
n
)
,
n
>
1
T(n)=\left\{ \begin{array}{rcl} 1, & & {n = 1}\\ 2T(\frac{n}{2})+O(n), & & {n > 1}\\ \end{array} \right.
T(n)={1,2T(2n)+O(n),n=1n>1
所以,使用主定理,可以直接求得
T
(
n
)
=
n
l
o
g
n
T(n)=nlogn
T(n)=nlogn。