寻找最大子数组和|豆包MarsCode AI刷题

60 阅读5分钟

寻找最大子数组和|豆包MarsCode AI刷题

一、题目概述

本题要求在给定一个长度为 N 的数组 data_array 中,通过允许翻转数组中的任意子数组这一操作,找出在所有可能情况(包括原始数组和翻转后的数组)下的最大子数组和。

二、核心算法 - Kadane 算法

  1. 算法原理

    • Kadane 算法用于解决最大子数组和问题,其核心思想是动态规划。通过一次遍历数组,在遍历过程中不断更新两个关键变量:max_ending_here 和 max_so_far
    • max_ending_here 表示当前位置为止包含当前元素的最大子数组和。对于数组中的每个元素 xmax_ending_here 取 x 和 max_ending_here + x 中的较大值。这意味着要么以当前元素开始一个新的子数组(即只取当前元素 x),要么将当前元素加入到之前的子数组中(取 max_ending_here + x)。
    • max_so_far 则记录遍历到当前位置时全局的最大子数组和,它在每一步都取当前的 max_so_far 和新计算出的 max_ending_here 中的较大值,从而保证 max_so_far 始终是全局最大子数组和。
  2. 代码实现

收起

python

复制

def kadane(arr):
    max_ending_here = max_so_far = arr[0]
    for x in arr[1:]:
        max_ending_here = max(x, max_ending_here + x)
        max_so_far = max(max_so_far, max_ending_here)
    return max_so_far

三、整体解题思路

  1. 计算原始数组的最大子数组和

    • 首先调用 kadane 算法计算原始数组 data_array 的最大子数组和,并将其存储在 max_sum_original 变量中。这是后续比较的一个基准值,因为翻转后的最大子数组和可能不超过原始数组的情况。
  2. 遍历所有可能的子数组进行翻转并计算最大子数组和

    • 使用两层循环遍历数组,外层循环变量 i 从 0 到 N - 1,内层循环变量 j 从 i 到 N - 1。这样可以遍历到数组中的所有子数组。
    • 对于每一个子数组(索引从 i 到 j),将其翻转得到 flipped_subarray,然后创建一个新数组 new_array,它由原数组中索引在 i 之前的部分、翻转后的子数组 flipped_subarray 和原数组中索引在 j + 1 之后的部分拼接而成。
    • 接着再次调用 kadane 算法计算新数组 new_array 的最大子数组和 max_sum_new
    • 将 max_sum_new 与当前记录的最大翻转后和 max_sum_after_flip 进行比较,如果 max_sum_new 更大,则更新 max_sum_after_flip
  3. 返回最终结果

    • 最后返回 max_sum_after_flip,它就是在所有可能的翻转情况以及原始状态下的最大子数组和。

四、代码示例与分析

收起

python

复制

def solution(N, data_array):
    # Kadane's algorithm to find maximum subarray sum
    def kadane(arr):
        max_ending_here = max_so_far = arr[0]
        for x in arr[1:]:
            max_ending_here = max(x, max_ending_here + x)
            max_so_far = max(max_so_far, max_ending_here)
        return max_so_far

    # Calculate the maximum subarray sum of the original array
    max_sum_original = kadane(data_array)

    # Initialize the maximum sum after possible flips
    max_sum_after_flip = max_sum_original

    # Iterate over all possible subarrays to find the maximum sum after flipping
    for i in range(N):
        for j in range(i, N):
            # Flip the subarray from index i to j
            flipped_subarray = data_array[i:j+1][::-1]
            # Create a new array with the flipped subarray
            new_array = data_array[:i] + flipped_subarray + data_array[j+1:]
            # Calculate the maximum subarray sum of the new array
            max_sum_new = kadane(new_array)
            # Update the maximum sum after flip
            max_sum_after_flip = max(max_sum_after_flip, max_sum_new)

    return max_sum_after_flip

在上述代码中:

  • solution 函数是整个解题的入口函数,它首先计算原始数组的最大子数组和,然后通过嵌套循环遍历所有可能的子数组翻转情况,计算新数组的最大子数组和并更新最大翻转后和,最后返回结果。
  • 例如对于测试用例 [1,2,3,-1,4],原始数组的最大子数组和为 10(即整个数组)。在遍历翻转情况时,虽然有一些子数组翻转后会改变子数组和,但最终最大的仍然是原始数组的 10,所以最终结果为 10。对于 array1 数组,经过所有可能的翻转和计算后,得到最大子数组和为 1348

五、时间复杂度分析

  • 计算原始数组的最大子数组和使用 kadane 算法,时间复杂度为O(n) ,其中 N 是数组的长度。
  • 对于遍历所有可能的子数组翻转情况,外层循环执行 N 次,内层循环对于每个外层循环执行 N - i 次,所以总共大约执行n方次操作。每次操作包括翻转子数组、创建新数组和计算新数组的最大子数组和(使用 kadane 算法,时间复杂度为O(n) ),所以这部分的时间复杂度为 O(n三次方)。
  • 总体时间复杂度为 ,由于存在三层循环嵌套(两层用于遍历子数组,一层在 kadane 算法中遍历新数组)。

六、空间复杂度分析

  • 代码中主要的空间消耗在于创建新数组 new_array 和存储翻转后的子数组 flipped_subarray。在最坏情况下,创建的新数组和翻转子数组的总长度可能接近数组长度的平方(因为每次都可能创建一个接近原数组长度的新数组),所以空间复杂度为O(n) 。但如果考虑原地翻转(不创建新的 flipped_subarray 变量),空间复杂度可以优化到 O(n平方),因为只需要额外的空间来存储一些指针和临时变量用于计算最大子数组和。

综上所述,本题通过结合 Kadane 算法和对子数组的全面遍历翻转,有效地解决了寻找最大子数组和(含翻转操作)的问题,但在时间和空间复杂度上有一定的代价,可以进一步思考优化策略,如是否可以减少不必要的计算或降低空间消耗。