918.环形子数组的最大和

原题链接

题目描述:

请添加图片描述

示例:

在这里插入图片描述

提示:

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

相似题目: 最大子数组和

解题思路:

方法一:动态规划

由于数组是环形的,所以最大子数组的形势会出现两种情况:

  • 情况一:

    ​最大子数组只有一段,即 [i, j] { i >= 0 && j < len && i < j}

  • 情况二:

    ​ 最大子数组出现两段, [0, i] 和 [j, len - 1] { i < j}
    在这里插入图片描述

情况一解法:

针对情况一的解法和最大子数组和一样,同样使用动态规划。

  1. 定义数组dp = new int[len], 其中dp[i] 表示 以下标i结尾的最大子数组的和。

  2. 推导递推公式:

    当遍历到下标 i 时,以下标 i 结尾的最大子数组的和应该为多少?

    dp[i] 取决于 dp[i - 1] 和 nums[i],其中dp[i - 1] 表示 以下标 i - 1 结尾的最大子数组的和,所以dp[i] = dp[i - 1] + nums[i], 但是nums[i] 如果比dp[i - 1] + nums[i]还要大,那dp[i] = nums[i], 所以 dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])。

  3. 初始化数组dp:

    通过递推公式可知dp[i] 是由前一个推导出来的,所以dp[0] 必须初始化,dp[0] = nums[0]。

  4. 确定遍历顺序:

    从前往后遍历即可。

public int maxSumOfSubArray(int[] nums){
    int len = nums.length;
    int[] dp = new int[len];
    dp[0] = nums[0];
    int ans = dp[0];
    for(int i = 1; i < len; i++){
        dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
        ans = Math.max(dp[i], ans);  // 更新结果。
    }
    return ans;
}

情况二解法:

我们需要记录[0, i] 区间的最大值,这个区间是从0开始的,比方说[0, 2]区间的最大值为10,如果[0, 3]区间的和小于10,那么[0, 3]区间的值为[0, 2]区间的值。

记录[0, i]区间的最大值可以在情况一中记录,如下:

public int maxSumOfSubArray(int[] nums){
    int len = nums.length;
    int[] dp = new int[len];
    dp[0] = nums[0];
    int ans = dp[0];
    //=========记录[0, i]区间最大值===================
   	int[] leftSum = new int[len];
    leftSum[0] = nums[0];   
    int preSum = nums[0];
    //==============================================
    for(int i = 1; i < len; i++){
        dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
        ans = Math.max(dp[i], ans);  // 更新结果。
        //========记录[0, i]区间最大值==================
        preSum += nums[i]; // 记录[0, i] 区间的和。
        leftSum[i] = Math.max(leftSum[i - 1], preSum);
    }
    return ans;
}

接下来,倒序遍历nums数组,记录[j, len - 1]的和。[j, len - 1]的和加上 leftSum[j - 1]就是情况二最大子数组的和

完整代码如下:

public int maxSubarraySumCircular01(int[] nums) {
    int len = nums.length;
    int ans = nums[0];
    int[] dp = new int[len];
    dp[0] = nums[0];
    int[] leftSum = new int[len];
    leftSum[0] = nums[0];
    int preSum = nums[0];
    for (int i = 1; i < len; i++) {
        dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
        ans = Math.max(dp[i], ans);
        preSum += nums[i];
        leftSum[i] = Math.max(leftSum[i - 1], preSum);
    }

    int backSum = 0; // 记录[j, len - 1]的和
    for (int j = len - 1; j > 0; j--) {
        backSum += nums[j];
        ans = Math.max(ans, backSum + leftSum[j - 1]);
    }
    return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值