918. 环形子数组的最大和(动态规划)

环形子数组的最大和(力扣)

题目描述

给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。

环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 。

子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], …, nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 。

示例 1:

输入:nums = [1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3

示例 2:

输入:nums = [5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10

提示:

n == nums.length
1 <= n <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104​​​​​​​

分析

数组nums是一个环形数组,当最大子数组在内部时候,直接计算最大子数组和就行了,
如果最大子数组在两端的时候,我们只需要转换计算最小子数组和就行了,因为:
两端的数组和+内部的数组和=总的数组和
我们只需要计算内部的最小子数组和,就可以让两端的子数组和达到最大子数组和,因为总的数组和是一个固定不变的数字。

我们需要维护以下几个变量:
maxS=Integer.MIN_VALUE;maxF=0;minS=0;minF=0;
依次表示:最大子数组和,选择当前数字的最大和,最小数组和,和选择当前数字最小的和。

maxF是一个过渡值,遍历到当前元素值a的时候,
选择加不加前面的子数组的值,表示的是选择当前元素的最大的和

minF是一个过渡值,遍历到当前元素值a的时候,
选择加不加前面的子数组的值,表示的是选择当前元素的最小的和

一般来说这时候就可以直接返回Math.max(maxS,sum-minS)了,但是存在需要特判的情况。

需要特判的情况的nums数组:
nums[-3,-2,-3] nums=[−1,1,−1]
当数组如同这种的时候,sum和minS的大小相同,maxS在数组内部,是不会去到边界的
,如果直接返回:Math.max(maxS,sum-minS),那么第一个数组nums[-3,-2,-3]就会出错,返回0,而我们一应该返回maxS=-2,虽然第二种不会出错,但是我们针对这种情况sum和minS的大小相同,我们直接返回maxS(最大子数组和)即可。

测试代码


class Solution {
    public int maxSubarraySumCircular(int[] nums) {
        
        int maxS=Integer.MIN_VALUE,maxF=0,minS=0,minF=0;
        int sum=0;
        for (int a:nums) {
            //maxF是一个过渡值,遍历到当前元素值a的时候
            // 选择加不加前面的子数组的值,表示的是选择当前元素的最大的和
            //maxF是正数则要,负数则不要
            maxF=Math.max(maxF,0)+a;
            //maxF和maxS作比较
            maxS=Math.max(maxF,maxS);

            //minF是一个过渡值,遍历到当前元素值a的时候
            //选择加不加前面的子数组的值,表示的是选择当前元素的最小的和
            //maxF是负数则要,正数则不要
            minF=Math.min(minF,0)+a;
            minS=Math.min(minF,minS);

            //计算数组的总和
            sum+=a;
        }

        //判断是否存在minS==sum,然后返回正确答案
        return minS==sum?maxS:Math.max(maxS,sum-minS);
    }
}

复杂度分析

时间复杂度:O(n), n为数组的长度,for循环遍历了一遍。
空间复杂度:O(1)。

测试结果
918. 环形子数组的最大和(动态规划)_第1张图片

你可能感兴趣的:(leetcode题目,leetcode,算法,数据结构)