算法复习——分而治之篇之最大子数组问题

算法复习——分而治之篇之最大子数组问题

以下内容主要参考中国大学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+54+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(lr)的非空子数组,其和记为 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

猜你喜欢

转载自blog.csdn.net/NickHan_cs/article/details/111601055