第四章 分治策略
4.1 最大子数组问题
def find_max_crossing_subarray(A, low, mid, high):
"""
查找跨越中点的最大子数组
核心思路:
- 从中点向左扫描,找到包含mid的最大左侧和
- 从中点+1向右扫描,找到包含mid+1的最大右侧和
- 两者相加就是跨越中点的最大子数组和
参数:
A (list): 输入数组
low (int): 数组的起始索引
mid (int): 中间索引
high (int): 数组的结束索引
返回:
tuple: (max_left, max_right, total_sum)
分别表示最大子数组的左边界、右边界和总和
"""
# === 第一步:向左扫描,找到包含mid的最大左侧和 ===
left_sum = float('-inf') # 初始化为负无穷,确保能被第一个值更新
current_sum = 0 # 当前累计和
max_left = mid # 记录左边界位置
# 从mid开始向左遍历到low(包含边界)
for i in range(mid, low - 1, -1): # 注意:low-1是为了包含low
current_sum += A[i] # 累加当前元素
if current_sum > left_sum: # 如果当前和更大
left_sum = current_sum # 更新最大左侧和
max_left = i # 更新左边界位置
# === 第二步:向右扫描,找到包含mid+1的最大右侧和 ===
right_sum = float('-inf') # 初始化为负无穷
current_sum = 0 # 重置累计和
max_right = mid + 1 # 记录右边界位置
# 从mid+1开始向右遍历到high(包含边界)
for j in range(mid + 1, high + 1): # 注意:high+1是为了包含high
current_sum += A[j] # 累加当前元素
if current_sum > right_sum: # 如果当前和更大
right_sum = current_sum # 更新最大右侧和
max_right = j # 更新右边界位置
# === 返回跨越中点的最大子数组信息 ===
return (max_left, max_right, left_sum + right_sum)
def find_maximum_subarray(A, low, high):
"""
使用分治策略查找最大子数组
分治思路:
1. 基本情况:只有一个元素时,该元素就是最大子数组
2. 递归情况:将数组分为左右两部分,分别求解
3. 合并阶段:比较左半部分、右半部分和跨越中点三种情况的最大值
参数:
A (list): 输入数组
low (int): 起始索引
high (int): 结束索引
返回:
tuple: (start_index, end_index, max_sum)
最大子数组的起始位置、结束位置和最大和
"""
# === 基本情况:递归终止条件 ===
if high == low:
# 只有一个元素时,该元素本身就是最大子数组
return (low, high, A[low])
else:
# === 分解阶段:将问题分解为子问题 ===
mid = (low + high) // 2 # 计算中点,整数除法向下取整
# 递归求解左半部分 [low, mid] 的最大子数组
left_low, left_high, left_sum = find_maximum_subarray(A, low, mid)
# 递归求解右半部分 [mid+1, high] 的最大子数组
right_low, right_high, right_sum = find_maximum_subarray(A, mid + 1, high)
# 求解跨越中点的最大子数组
cross_low, cross_high, cross_sum = find_max_crossing_subarray(A, low, mid, high)
# === 合并阶段:比较三种情况,选择最优解 ===
if left_sum >= right_sum and left_sum >= cross_sum:
# 左半部分包含最大子数组
return (left_low, left_high, left_sum)
elif right_sum >= left_sum and right_sum >= cross_sum:
# 右半部分包含最大子数组
return (right_low, right_high, right_sum)
else:
# 跨越中点的子数组是最大子数组
return (cross_low, cross_high, cross_sum)
Exercise 4.1-2
暴力法代码
def find_max_subarray_bruteforce(A):
"""
暴力枚举法查找最大子数组和
参数:
A (list): 输入整数数组
返回:
tuple: (start_index, end_index, max_sum)
"""
n = len(A)
max_sum = float('-inf')
max_left = 0
max_right = 0
for i in range(n):
current_sum = 0
for j in range(i, n):
current_sum += A[j]
if current_sum > max_sum:
max_sum = current_sum
max_left = i
max_right = j
return (max_left, max_right, max_sum)
Exercise 4.1-4
假定修改最大子数组问题的定义,允许结果为空子数组,其和为0。你应该如何修改现算法, 使它们能允许空子数组为最终结果?
- 预处理:线性扫描数组
- 检查是否存在正数或零。
- 如果没有(即全为负数),直接返回
(None, None, 0)。
- 否则:使用分治策略查找最大子数组
- 调用递归函数
find_max_subarray查找最大子数组
- 调用递归函数
def find_max_subarray_or_empty(A):
"""
查找最大子数组。若数组全为负数,则返回空子数组和 0。
参数:
A (list): 输入整数数组
返回:
tuple: (start_index, end_index, max_sum)
若全为负数,则返回 (None, None, 0)
"""
# 判断是否有非负数(即大于等于 0 的数)
has_positive = any(x >= 0 for x in A)
if not has_positive:
return (None, None, 0)
# 否则使用分治法查找最大子数组
start, end, sum_val = find_max_subarray(A, 0, len(A) - 1)
return (start, end, sum_val)
Exercise 4.1-5
为最大子数组问题设计一个非递归的线性时间算法(Kadane算法) ,其核心思想是:
从左到右扫描数组,维护当前子数组的最大和。
每一步决定是扩展当前子数组,还是以当前元素为新子数组的起点。
def kadane_with_indices(A):
"""
使用 Kadane 算法查找最大子数组(非递归、线性时间)
核心思路:
- 维护当前子数组的和 current_sum
- 在每个位置决定:继续扩展 vs 重新开始
- 同时跟踪全局最大值和对应的起止位置
参数:
A (list): 输入整数数组
返回:
tuple: (start_index, end_index, max_sum)
若数组全为负数,则返回 (None, None, 0)
"""
n = len(A)
# === 边界情况处理 ===
if n == 0:
return (None, None, 0)

最低0.47元/天 解锁文章
4552

被折叠的 条评论
为什么被折叠?



