青训营X豆包MarsCode 技术训练营:AI刷题 问题92:子数组和的最大值问题| 豆包MarsCode AI 刷题

65 阅读6分钟

问题描述

小U手上有一个整数数组,他想知道如果从数组中删除任意一个元素后,能得到的长度为 k 的子数组和的最大值。你能帮小U计算出这个结果吗?
如果数组恰好为 k 个元素,那么不进行删除操作。


测试样例

样例1:

输入:n = 5,k = 3,nums = [2, 1, 3, -1, 4]
输出:8

样例2:

输入:n = 6,k = 2,nums = [-1, -1, 5, -2, 3, 4]
输出:8

样例3:

输入:n = 4,k = 2,nums = [-5, -3, 2, 1]
输出:3

问题解析

要解决这个问题,我们需要找到在删除一个元素后,数组中能够得到的长度为 k 的子数组和的最大值。我们可以使用滑动窗口算法来高效地计算这一点。

解决思路:

  1. 无删除操作的情况: 首先,我们计算在不删除任何元素时,长度为 k 的子数组和的最大值。使用滑动窗口可以在 O(n) 时间内计算所有长度为 k 的子数组的和。

  2. 删除一个元素的情况: 对于每个可能被删除的元素,我们需要:

    • 对数组进行一次滑动窗口计算,考虑删除当前元素后的长度为 k 的子数组。
    • 在删除元素后,滑动窗口的范围会变为 k 个元素,但跳过被删除的元素。
  3. 比较这两种情况: 最终,我们会比较无删除和删除一个元素后的最大和。

实现步骤:

  1. 计算没有删除的情况:使用滑动窗口算法计算所有长度为 k 的子数组和。
  2. 删除每个元素的情况:对每个元素,删除它后再计算可能的最大子数组和。

代码实现

def maxSumAfterOneDeletion(n, k, nums):
    # Step 1: Compute the sum of all length k subarrays using a sliding window.
    window_sum = sum(nums[:k])
    max_sum = window_sum  # Maximum sum without deleting any element
    
    # Step 2: Calculate the sum for all k-length subarrays after deleting one element.
    for i in range(k, n):
        # Sliding window for the subarray from i-k+1 to i
        window_sum += nums[i] - nums[i - k]
        max_sum = max(max_sum, window_sum)
    
    # Step 3: Try deleting each element and calculate the maximum sum
    max_sum_after_deletion = float('-inf')
    
    for i in range(n):
        # Create a new list without the element nums[i]
        new_nums = nums[:i] + nums[i+1:]
        
        # Recalculate sliding window sum for the new list
        if len(new_nums) >= k:
            window_sum = sum(new_nums[:k])
            max_sum_after_deletion = max(max_sum_after_deletion, window_sum)
            
            for j in range(k, len(new_nums)):
                window_sum += new_nums[j] - new_nums[j - k]
                max_sum_after_deletion = max(max_sum_after_deletion, window_sum)
    
    return max(max_sum, max_sum_after_deletion)

代码解析

1. 函数定义:

def maxSumAfterOneDeletion(n, k, nums):
  • n: 数组的长度
  • k: 子数组的长度
  • nums: 给定的整数数组

我们需要在给定的数组中,考虑删除一个元素后,找到长度为 k 的子数组和的最大值。

2. 计算不删除任何元素时的最大子数组和

window_sum = sum(nums[:k])
max_sum = window_sum  # 最大和初始化为前k个元素的和
  • 这部分代码初始化了一个滑动窗口,计算数组前 k 个元素的和。window_sum 变量保存当前窗口的和。
  • 然后,我们将 max_sum 初始化为不删除任何元素时的最大子数组和,即前 k 个元素的和。

接下来,我们使用滑动窗口的方式来计算所有长度为 k 的子数组的和:

for i in range(k, n):
    window_sum += nums[i] - nums[i - k]
    max_sum = max(max_sum, window_sum)
  • ikn-1,代表了从第 k 个元素开始,滑动窗口向右移动。
  • 对于每次滑动,我们通过 window_sum += nums[i] - nums[i - k] 更新当前窗口的和。即我们从窗口中去掉了元素 nums[i - k],并加入了元素 nums[i],这样就能计算出新的窗口和。
  • 之后,我们更新 max_sum,确保保存当前最大值。

3. 删除一个元素后的最大子数组和

接下来,我们考虑删除数组中的每个元素。对于每个删除的元素,我们需要重新计算移除该元素后的所有长度为 k 的子数组的和。

max_sum_after_deletion = float('-inf')  # 用于记录删除一个元素后的最大和
  • max_sum_after_deletion 初始化为负无穷,用于保存删除一个元素后可能的最大子数组和。

然后,我们遍历数组中的每个元素,模拟删除该元素的情况:

for i in range(n):
    new_nums = nums[:i] + nums[i+1:]
  • 这里,new_nums 是一个新数组,它是原数组 nums 删除第 i 个元素后的结果。我们通过 nums[:i]nums[i+1:] 将原数组切分为两部分,合并起来。

对于每一个删除后的新数组,我们再次使用滑动窗口方法计算其所有长度为 k 的子数组的和:

if len(new_nums) >= k:
    window_sum = sum(new_nums[:k])
    max_sum_after_deletion = max(max_sum_after_deletion, window_sum)
    
    for j in range(k, len(new_nums)):
        window_sum += new_nums[j] - new_nums[j - k]
        max_sum_after_deletion = max(max_sum_after_deletion, window_sum)
  • 对于每个删除元素后的新数组 new_nums,如果新数组的长度大于或等于 k,我们计算其前 k 个元素的和,并通过滑动窗口计算所有长度为 k 的子数组和的最大值。
  • 我们用 window_sum += new_nums[j] - new_nums[j - k] 来更新窗口和,和之前一样,滑动窗口逐步计算所有可能的子数组和。

4. 计算最终的结果

最后,返回 max(max_sum, max_sum_after_deletion),即比较不删除元素和删除一个元素后的最大子数组和,返回其中较大的那个值。

return max(max_sum, max_sum_after_deletion)

时间复杂度分析:

  • 滑动窗口计算最大子数组和

    • 对于每个删除元素后的新数组,重新计算子数组和需要 O(n) 的时间。
    • 在最坏的情况下,我们需要删除每个元素,循环次数是 n,因此删除一个元素后的子数组和计算总共需要 O(n^2) 的时间。
  • 整体时间复杂度:最坏情况下,整体的时间复杂度是 O(n^2)。由于对于每个元素我们都要创建新的数组并重新计算子数组和。

优化:

这种实现方法的时间复杂度是 O(n^2),可以进一步优化。如果数组不大,可以使用更高效的方式处理删除操作,比如考虑从滑动窗口的角度优化。

总结:

  • 代码通过滑动窗口计算不删除元素时的最大子数组和。
  • 对于每个元素,模拟删除它,计算删除后的最大子数组和。
  • 最终返回两者的较大值作为结果。