AI刷题92 子数组和的最大值问题| 豆包MarsCode AI刷题

41 阅读4分钟

题目解析

题目描述

小C有一个整数数组,想知道如果从数组中删除一个元素后,能得到长度为 k 的子数组,其子数组和的最大值是多少。如果数组的大小恰好为 k,则不需要删除任何元素。

输入

  • 一个整数 n:表示数组的长度;
  • 一个整数 k:表示目标子数组的长度;
  • 一个整数数组 nums:表示原始数组。

输出

  • 删除一个元素后,能够得到的长度为 k子数组的最大和。

测试样例

  • 示例 1:
    输入:n=5,k=3,nums=[2,1,3,−1,4]
    输出:8 解释:删除 −1后,子数组 [1,3,4]的和为 8,是最大值。
  • 示例 2:
    输入:n=6,k=2,nums=[−1,−1,5,−2,3,4]
    输出:8
    解释:删除 −2 后,子数组 [5,3] 的和为 8。

题目分析

关键思路

  1. 直接求解

    • 对于每一个元素,假设将其从数组中删除,求剩下数组的长度为 k 的子数组的最大和。
    • 遍历每一种可能,选出最大值。
  2. 前缀和优化

    • 使用前缀和数组快速计算子数组和。
    • 子数组和计算公式:subsum=prefix[j]−prefix[i−1]。
  3. 边界条件

    • 如果 n=k,不需要删除元素,直接求整个数组的和。

算法步骤

  1. 前缀和数组

    • 构造一个前缀和数组,用于快速计算子数组的和。
  2. 遍历删除操作

    • 模拟删除数组中的每一个元素,构造删除后的数组。
    • 对于删除后的数组,找到长度为 k的子数组和的最大值。
  3. 记录结果

    • 每次删除操作后,记录长度为 k 子数组和的最大值,并更新全局最大值。

代码详解

以下是给出的 Java 实现代码:

public class Main {
    public static int solution(int n, int k, int[] nums) {
        // 前缀和数组
        int[] num = new int[n + 1];
        int[] temp = new int[n - 1];
        int max = Integer.MIN_VALUE;
        int z = 0;

        // 构造前缀和数组
        for (int i = 1; i <= n; i++) {
            num[i] = nums[i - 1] + num[i - 1];
        }

        // 如果数组长度恰好为 k,直接返回整个数组和
        if (n == k) {
            return num[n];
        }

        // 遍历每一个删除的元素
        for (int i = 0; i < n; i++) {
            z = 0;

            // 删除第 i 个元素,构造删除后的数组 temp
            for (int j = 0; j < n; j++) {
                if (i != j) {
                    temp[z++] = nums[j];
                }
            }

            // 计算删除后的数组的最大子数组和
            if (subNUm(temp, k) > max) {
                max = subNUm(temp, k);
            }
        }
        return max;
    }

    // 计算长度为 k 的子数组的最大和
    public static int subNUm(int[] nums, int k) {
        int max = Integer.MIN_VALUE;
        int[] num = new int[nums.length + 1];

        // 构造前缀和数组
        for (int i = 1; i <= nums.length; i++) {
            num[i] = nums[i - 1] + num[i - 1];
        }

        // 通过前缀和快速计算子数组和
        for (int i = 0; i < nums.length; i++) {
            if (i + k <= nums.length) {
                if (num[i + k] - num[i] > max) {
                    max = num[i + k] - num[i];
                }
            } else {
                break;
            }
        }
        return max;
    }

    public static void main(String[] args) {
        // 测试样例
        System.out.println(solution(5, 3, new int[] { 2, 1, 3, -1, 4 }) == 8);
    }
}

代码解析

  1. 主函数 solution

    • 构造前缀和数组,用于快速计算子数组和。
    • 遍历数组,删除每个元素,并调用 subNum 函数计算删除后的数组的最大子数组和。
  2. 辅助函数 subNum

    • 输入一个数组和目标长度 k,通过前缀和方法快速计算长度为 k 的子数组的最大和。
  3. 关键点

    • 前缀和数组的构造:通过累积和快速计算子数组和。
    • 删除元素的模拟:使用双循环实现删除操作,效率稍低,但逻辑简单。

复杂度分析

  1. 时间复杂度

    • 主函数中有两层循环,分别是删除元素的 O(n) 和计算子数组和的 O(n−k)。因此总时间复杂度为 O(n^2)。
  2. 空间复杂度

    • 额外的前缀和数组和临时数组,占用 O(n)空间。

虽然时间复杂度较高,但在数据规模有限时表现良好。如果需要进一步优化,可以使用滑动窗口技术减少部分重复计算。


个人分析与优化

  1. 滑动窗口优化

    • 在删除元素后,可以直接用滑动窗口技术代替前缀和来计算子数组和,减少重复计算。
  2. 特殊情况优化

    • 当数组长度 n=k时,直接返回数组和,避免不必要的计算。
  3. 改进思路

    • 如果 k较大,可以优先删除负数元素,保证剩下的子数组和更高。

总结

本题综合了前缀和、滑动窗口等算法思想,涉及数组的局部优化和全局搜索。通过本文,我们学习到以下内容:

  1. 如何使用前缀和快速求解子数组和。
  2. 如何通过模拟删除操作解决全局优化问题。
  3. 如何分析代码的复杂度,并寻找优化空间。

这类问题在实际场景中有广泛应用,比如子序列优化、滑动窗口统计等,掌握这些技巧能帮助我们更高效地解决类似问题!