Blage's Coding Blage's Coding
Home
算法
  • 手写Spring
  • SSM
  • SpringBoot
  • JavaWeb
  • JAVA基础
  • 容器
  • Netty

    • IO模型
    • Netty初级
    • Netty原理
  • JVM
  • JUC
  • Redis基础
  • 源码分析
  • 实战应用
  • 单机缓存
  • MySQL

    • 基础部分
    • 实战与处理方案
    • 面试
  • ORM框架

    • Mybatis
    • Mybatis_Plus
  • SpringCloudAlibaba
  • MQ消息队列
  • Nginx
  • Elasticsearch
  • Gateway
  • Xxl-job
  • Feign
  • Eureka
  • 面试
  • 工具
  • 项目
  • 关于
🌏本站
🧸GitHub (opens new window)
Home
算法
  • 手写Spring
  • SSM
  • SpringBoot
  • JavaWeb
  • JAVA基础
  • 容器
  • Netty

    • IO模型
    • Netty初级
    • Netty原理
  • JVM
  • JUC
  • Redis基础
  • 源码分析
  • 实战应用
  • 单机缓存
  • MySQL

    • 基础部分
    • 实战与处理方案
    • 面试
  • ORM框架

    • Mybatis
    • Mybatis_Plus
  • SpringCloudAlibaba
  • MQ消息队列
  • Nginx
  • Elasticsearch
  • Gateway
  • Xxl-job
  • Feign
  • Eureka
  • 面试
  • 工具
  • 项目
  • 关于
🌏本站
🧸GitHub (opens new window)
  • 数组

  • 链表

  • 字符串

  • 二叉树

  • 动态规划

  • 深搜回溯

  • 数学贪心

  • 堆栈队列

    • 394.字符串解码
    • 32. 最长有效括号
    • 739. 每日温度
    • 1130. 叶值的最小代价生成树
    • 84. 柱状图中最大的矩形
    • 42.接雨水
    • 85. 最大矩形
    • 239.滑动窗口最大值
    • 6891. 机器人碰撞
    • 445. 两数相加 II
    • 2762. 不间断子数组
    • 6919. 使数组中的所有元素都等于零
      • 1.滑动窗口+队列
        • 将问题转化为滑动窗口
        • 从问题出发思考+差分数组思想
      • 2.举例
    • 862. 和至少为 K 的最短子数组
    • 1499. 满足不等式的最大值
    • 2856. 删除数对后的最小数组长度
    • 901. 股票价格跨度
    • 2034. 股票价格波动
    • 1488. 避免洪水泛滥
    • 2940. 找到 Alice 和 Bob 可以相遇的建筑
    • 2454. 下一个更大元素 IV
  • 前缀和

  • 算法设计

  • 位运算

  • WA

  • 算法
  • 堆栈队列
phan
2023-07-09
目录

6919. 使数组中的所有元素都等于零

# 6919. 使数组中的所有元素都等于零 (opens new window)

# 1.滑动窗口+队列

# 将问题转化为滑动窗口

基本思想:用双端队列保存滑动窗口内的所有值,规定大小为k的滑动窗口从左往右移动,因此当前滑动窗口左边界元素值m变为0的扣减操作只能在当前滑动窗口进行(因为窗口下次向右移动一个位置后,m再也没有机会执行扣减操作了)。假设窗口内的所有元素减去m后存在小于0的值,那么返回false。

这里如果是传统做法,每次窗口移动令所有元素都减去m,然后再进行判断,那么时间复杂度O(m*k)会超时。因此本题难点就在于如何简化“扣减”和“判断”操作?扣减时能否只算一次?

# 从问题出发思考+差分数组思想

“窗口移动判断”操作问题本质:对于窗口【i,i+k】,如果移动后窗口内的最大值小于等于num[i+1+k],说明可以加入窗口。最大值即为num[i+k]扣减掉将第i个位置化零的值,因此问题关键就是计算出当前窗口向右移一格后窗口内的元素应该扣减多少?

  • 第一个元素nums[0]所在的窗口移动后,则直接扣减nums[0](前面说过它只能在这个窗口化零)
  • i>0,nums[i]刚加入窗口时,假设i-1,i位置的值分别为a1,a2,窗口移动到左边界为 i 时,第i个位置上的值为a2-k-(a1-k)=a2-a1,也就是下一次移动需要扣减的值。这里根据i的范围又分两种情况:
    • 0<i<k,则a1=nums[i-1],a2=nums[i]
    • i>=k,则a1=nums[i-1] - 上一次移动扣减的值,a2=nums[i]

队列queue【i】保存的元素是当queue【i】移动到queue【0】时,整个窗口向右移动一个位置后nums窗口内每个元素需要扣减的值。而之所以能够这样做,是因为具备以下条件:

  • 判断新元素能否加入队列,只会用到一次队尾元素tail进行一次扣减运算得到的结果来作比较。
  • 后续滑动窗口内元素在移动的过程中,每次扣减队首元素得到的值并不需要关心和统计。

因此对于下一个元素k,只需要用窗口尾元素减去队列首元素得到差n,如果n大于k,则说明k在后面的扣减过程一定会变为负数;否则将滑动窗口向右移一个位置。

✨而这里每次新入队元素的值,实际上就是差分数组的计算过程。f[i]=nums[i]-nums[i-1]得到相邻两个数的差。

class Solution {
    public boolean checkArray(int[] nums, int k) {
        Deque<Integer> queue = new LinkedList<>();
        int i=1,pre=nums[0];
        queue.offerLast(nums[0]);
        //预处理先填满队列
        while (i<k) {
            if(nums[i]<nums[i-1]) return false;
            queue.offerLast((nums[i] - nums[i - 1]));
            i++;
        }
        
       //队列进出并判断
        for (int m = k; m < nums.length; m++) {
            int sub = queue.pollFirst();
            if(nums[m-1]-sub>nums[m]) return false;
            queue.offerLast(nums[m] + sub - nums[m-1]);
        }
        //判断剩下最后一个窗口是否能够化零
        queue.pollFirst();
        while(queue.size()>0){
            if(queue.pollFirst()!=0) return false;
        }
        return true;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 2.举例

nums=[3,4,5,6,3,2],k=4
1
  1. 初始化队列queue=[3,1,1,1]
  2. 窗口移动,整个窗口内的所有元素需要扣除队列头结点3,变为[0,1,2,3],窗口尾节点3小于等于nums下一个元素3,因此3可以加入当前滑动窗口,变为[1,2,3,3],显然当前一个3移出窗口后,后一个3肯定也变为0,因此队列加入元素0,queue=[1,1,1,0]
nums = [2,2,3,4,1,0], k = 3
1
  1. 初始化队列queue=[2,0,1]
  2. 窗口移动,整个窗口内的所有元素需要扣除队列头结点2,窗口元素变为[0,0,1],窗口尾节点1小于等于nums下一个元素4,因此4可以加入当前滑动窗口,变为[0,1,4],显然当前一个1移出窗口后,4的最大扣减数为1,因此队列加入元素3,queue=[0,1,3]
编辑 (opens new window)
#Leetcode#堆栈队列
上次更新: 2023/12/15, 15:49:57
2762. 不间断子数组
862. 和至少为 K 的最短子数组

← 2762. 不间断子数组 862. 和至少为 K 的最短子数组→

Theme by Vdoing | Copyright © 2023-2024 blageCoder
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式