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

154 阅读4分钟

问题描述

小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

解题思路

滑动窗口 + 枚举删除

1. 滑动窗口求初始最大和:

  • 遍历数组,找到所有长度为 k + 1(前提是数组长度 n != k,否则 k 不用加1) 的子数组的和。

2. 枚举删除一个元素:

  • 遍历当前滑动窗口中的所有元素,找到当前滑动窗口中的最小值,删除该最小值,用滑动窗口再次求解子数组的最大和。

3. 比较取最大值。

实现思路

  1. 初始化变量保存当前最大和 maxSum。
  2. 判断 数组长度 n 是否等于 k
  • 如果 n == k,不需要删除元素,输出数组所有的元素之和;
  • 如果 n != k,需要将 k + 1;因为我们的前提是删除一个元素之后的子数组和的最大值,
    • 被删除的这个元素可能是在一个子数组中间的某个元素,删除之后这个子数组长度就等于 k - 1 了,所以我们需要提前将 k + 1,如此在求得滑动窗口大小为 k + 1时,删除当前窗口的范围内的最小值之后的子数组长度还是 k。
    • 这个被删除的元素在子数组的左右边缘时,就算 k + 1了,也不会影响最后的结果。
  1. 遍历所有大小为 k 的滑动窗口: 删除当前滑动窗口的最小值之后,更新最大和 maxSum。

代码实现

public static int solution(int n, int k, int[] nums) {
        // 如果数组长度恰好为 k,不需要删除元素
        if (n == k) {
            int sum = 0;
            for (int num : nums) {
                sum += num;
            }
            return sum;
        }
        k++;

        // 滑动窗口计算长度为 k 的最大和
        int maxSum = Integer.MIN_VALUE, windowSum = 0, min = nums[0];
        for (int i = 0; i < k; i++) {
            windowSum += nums[i];
            min = Math.min(min, nums[i]);
        }
        maxSum = windowSum - min;

        // 枚举找到最小值
        for (int i = k; i < n; i++) {
            windowSum += nums[i] - nums[i - k];
            min = nums[i];
            for (int j = i - k + 1; j <= i; j++) {
                min = Math.min(min, nums[j]);
            }
            maxSum = Math.max(maxSum, windowSum - min);
        }

        return maxSum;
    }

时间复杂度

时间复杂度:O(n2)O(n^2)

  • 外层循环枚举删除每个元素:O(n)O(n)
  • 内层滑动窗口求和:O(nk)O(n - k)

空间复杂度:O(1)O(1)

学习心得

  1. 理解窗口的概念:滑动窗口是一个固定大小或动态大小的子数组,它沿着数组的某个方向移动。理解窗口如何移动和如何维护窗口内的状态是关键。
  2. 初始化窗口:在开始滑动之前,需要正确初始化窗口,包括计算窗口内元素的初始和或其他状态。
  3. 窗口的滑动:窗口的滑动通常涉及到两个操作:从窗口的起始位置移除一个元素,以及在窗口的末尾添加一个新元素。这两个操作需要同步进行,以保持窗口的大小不变。
  4. 状态更新:在窗口滑动时,需要更新窗口的状态,比如窗口内元素的和、最大值、最小值等。
  5. 记录结果:在滑动窗口的过程中,需要记录可能的结果,如最大和、最小和以及对应的窗口位置。
  6. 边界条件:处理边界条件非常重要,比如窗口的起始和结束位置,以及数组长度小于窗口大小时的情况。

知识点延伸

滑动窗口技术不仅仅局限于子数组和问题,它还可以应用于多种算法问题,以下是一些延伸知识点:

  1. 子串问题:滑动窗口常用于处理字符串相关的子串问题,如寻找最长无重复子串、最小覆盖子串等。

  2. 双指针技术:滑动窗口通常与双指针技术结合使用,其中一个指针指向窗口的起始位置,另一个指针指向窗口的结束位置。

  3. 动态窗口大小:在某些问题中,窗口的大小可能是动态变化的,这要求算法能够根据特定条件调整窗口的大小。

  4. 前缀和与哈希表:滑动窗口可以与前缀和或哈希表结合,用于快速计算窗口内元素的和或统计窗口内元素的频率。

  5. 性能优化:滑动窗口技术可以显著降低算法的时间复杂度,从O(nk)降低到O(n),这在处理大数据集时尤其重要。