算法导论第三版代码python实现与部分习题答案-第四章:分治策略(一)

第四章 分治策略

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。你应该如何修改现算法, 使它们能允许空子数组为最终结果?

  1. 预处理:线性扫描数组
    • 检查是否存在正数或零。
    • 如果没有(即全为负数),直接返回 (None, None, 0)
  2. 否则:使用分治策略查找最大子数组
    • 调用递归函数 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)  
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值