【LeetCode 算法】Maximum Sum Circular Subarray 环形子数组的最大和-单调队列

Maximum Sum Circular Subarray 环形子数组的最大和

问题描述:

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

环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 n u m s [ ( i + 1 ) m o d    n ] , n u m s [ i ] 的前一个元素是 n u m s [ ( i − 1 + n ) m o d    n ] nums[(i + 1) \mod n] , nums[i] 的前一个元素是 nums[(i - 1 + n) \mod n] nums[(i+1)modn]nums[i]的前一个元素是nums[(i1+n)modn]

子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 n u m s [ i ] , n u m s [ i + 1 ] , . . . , n u m s [ j ] ,不存在 i < = k 1 , k 2 < = j 其中 k 1 m o d    n = = k 2 m o d    n nums[i], nums[i + 1], ..., nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 \mod n == k2 \mod n nums[i],nums[i+1],...,nums[j],不存在i<=k1,k2<=j其中k1modn==k2modn

n = = n u m s . l e n g t h 1 < = n < = 3 ∗ 1 0 4 − 3 ∗ 1 0 4 < = n u m s [ i ] < = 3 ∗ 1 0 4 n == nums.length\\ 1 <= n <= 3 * 10^4\\ -3 * 10^4 <= nums[i] <= 3 * 10^4 n==nums.length1<=n<=31043104<=nums[i]<=3104

分析

基于上一篇的优先队列,可以很明显的看出时间复杂度很高,在该问题规模下,是非常有可能TLE的,虽然在昨天的提交中还可以勉强AC,但是理论上这个时间复杂度是不太理想的,和之前的 O ( N ) O(N) O(N)的时间复杂度更是无法比。

所以补一个 单调队列的思路。

目标同样是要找在固定下标i左侧的最小前缀和。
优先队列的处理关键是排序,而单调队列是基于更加巧妙的队列维护。

如果在下标 i i i左侧存在一个下标j处的前缀和是最小的。
此时子数组 [ j , i ] [j,i] [j,i]就是一个以i为右端点的最大和的子数组
如果此时 下标 i i i的前缀和 p r e s u m i presum_i presumi大于下标 j j j的前缀和 p r e s u m j presum_j presumj,即 p r e s u m i > p r e s u m j presum_i>presum_j presumi>presumj.那么 p r e s u m i presum_i presumi不可能 i i i右侧的其他下标 p r e s u m x presum_x presumx构成更大的子数组和,所以这个情况下, p r e s u m i presum_i presumi 不需要被考虑。

反之, p r e s u m i < = p r e s u m j presum_i<=presum_j presumi<=presumj,此时的 p r e s u m i presum_i presumi 相比较 p r e s u m j presum_j presumj可以与右侧的 p r e s u m x presum_x presumx构造更大的子数组和,那么 p r e s u m i presum_i presumi 就应该被考虑,

所以在队列的尾部,用 p r e s u m i presum_i presumi队尾 p r e s u m presum presum进行比较,如果发现 p r e s u m i presum_i presumi更加小或者相等,就可以将队尾元素出队。直到遇到比 p r e s u m i presum_i presumi更小的或者队列空了。

代码

单调队列

class Solution {
    public int maxSubarraySumCircular(int[] nums) {
        int n = nums.length,sum =0,ans = Integer.MIN_VALUE;
        // idx --- val
        Deque<int[]> dq = new ArrayDeque();
        dq.offerLast(new int[]{0,0});
        for(int i=0;i<2*n-1;i++){
            sum += nums[i%n];
            while(!dq.isEmpty()
            &&(i-dq.peekFirst()[0])>n-1){
                dq.pollFirst();
            }
            if(dq.peekFirst()[0]==n)break;
            if(!dq.isEmpty()){
                ans = Math.max(ans,sum -dq.peekFirst()[1]);
            }
            while(!dq.isEmpty()&&dq.peekLast()[1]> sum){
                dq.pollLast();
            }
            dq.offerLast(new int[]{i,sum});
        }
        return ans;
    }
} 

时间复杂度 O ( N ) O(N) O(N)

空间复杂度 O ( N ) O(N) O(N)

Tag

Array

Monotonic Queue

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eric.Cui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值