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

本文详细介绍了最大子数组问题的定义及其解决方案。采用分而治之的方法,通过递归求解左右子数组的最大子数组之和,进而找到跨越中点的最大子数组。最终通过伪代码给出了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

以下内容主要参考中国大学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

分而治之是一种常用的算法思想,可以用来解决最大子列和问题。在这种方法中,我们将问题分解成更小的子问题,并在每个子问题上递归地应用该方法。然后,我们将子问题的解合并起来,形成原始问题的解。 下面是使用分而治之方法解决最大子列和问题的Python代码: ```python def max_subarray_sum(arr, low, high): # 递归终止条件,当只有一个元素时返回该元素 if low == high: return arr[low] # 分治法的分解步骤,将问题分解为更小的子问题 mid = (low + high) // 2 left_max = max_subarray_sum(arr, low, mid) right_max = max_subarray_sum(arr, mid + 1, high) # 分治法的合并步骤,将子问题的解合并得到原始问题的解 cross_max = max_crossing_sum(arr, low, mid, high) # 返回左子数组最大子列和、右子数组最大子列和和跨越中点的最大子列和中的最大值 return max(left_max, right_max, cross_max) def max_crossing_sum(arr, low, mid, high): # 计算包含中点的左侧最大子列和 left_sum = float("-inf") curr_sum = 0 for i in range(mid, low - 1, -1): curr_sum += arr[i] if curr_sum > left_sum: left_sum = curr_sum # 计算包含中点的右侧最大子列和 right_sum = float("-inf") curr_sum = 0 for i in range(mid + 1, high + 1): curr_sum += arr[i] if curr_sum > right_sum: right_sum = curr_sum # 返回左侧最大子列和、右侧最大子列和和跨越中点的最大子列和的和 return left_sum + right_sum # 测试代码 arr = [-2, 11, -4, 13, -5, -2] max_sum = max_subarray_sum(arr, 0, len(arr) - 1) print(max_sum) ``` 这段代码通过递归地将问题分解为更小的子问题,并在每个子问题上应用该方法。然后,它将子问题的解合并起来,得到原始问题的解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值