LeetCode 862. 和至少为 K 的最短子数组

滑动窗口
滑动窗口遇到的最大的问题就是,在本题中出现负数,打破了滑动窗口中sum单调递增,因此会遇到很多的问题。
这里在维护窗口的同时,并修改在窗口里的值把nums[left] nums[right]中的值将负值nums[i]通过与左边的数相加放到左侧,来最终得到一个大于等于0的数(如果到了最左端还是小于0,那么就需要把这个负数抛弃掉),来放到最左侧来维护滑动窗口中的递增性。

可以把负值一直与左侧相加得到新的左侧的基础是,如果最后负数不能通过与左侧的数相加成为一个正数,则证明在nums[left] nums[right]nums[left] 到 nums[i]结果为负,如果此时sum >= k的话nums[i] 到 nums[right]一定sum >= k且比nums[left] nums[right]长度短

换而言之,对于找到最短的子数组来说,负数的出现,影响的是负数左边最近的正数,如果这个正数不在结果的窗口内,那么这些负数一定不在窗口内

class Solution {
public:
    int shortestSubarray(vector<int>& nums, int k) {
        int sum = 0;
        int left = 0;
        int res = nums.size() + 1;
        for(int right = 0; right < nums.size(); right++)
        {
            if(nums[right] >= k)
                return 1;
            sum += nums[right];
            if(sum <= 0)
            {
                left = right + 1;
                sum = 0;
                continue;
            }
            for(int j = right - 1; nums[j + 1] < 0; j--)
            {
                nums[j] += nums[j + 1];
                nums[j + 1] = 0;
            }
            if(sum < k)
                continue;
            while(sum - nums[left] >= k)
                sum -= nums[left++];
            res = min(res, right - left + 1);
        }
        return res == nums.size() + 1 ? -1 : res;
    }
};

双端队列
比较好理解了,对于新的要加入队列的数sum来说,有两种情况,
一种是sum - 队列最前面的和 >= k,这种情况就可以不用保留当前队列最前面的和了,因为它本身和他前面的数与sum相减一定是>= k的。因此需要弹出此时队列最前面的和并重复操作
一种是sum [i] < 队列最后面的和 [j],由于此时 队列最后面的和 [j]sum之前出现,因此如果某个 >=k的子串j - k从此时 队列最后面的和 [j]开始,则从sum[i]开始一定的子串i - k一定>=k且比
j - k短,因此需要弹出队列最后面的和 [j]并重复

class Solution {
public:
    int shortestSubarray(vector<int>& nums, int k) {
        deque<pair<long, int>> que;
        int res = nums.size() + 1;
        long sum = 0;
        que.push_back(pair<long, int>(sum, 0));
        for(int i = 0; i < nums.size(); i++)
        {
            sum += nums[i];
            while(!que.empty() && sum - que.front().first >= k)
            {
                res = min(res, i - que.front().second + 1);
                que.pop_front();
            }
            while(!que.empty() && sum <= que.back().first)
                que.pop_back();
            que.push_back(pair<long, int>(sum, i + 1));
        }
        return res == nums.size() + 1 ? -1 : res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值