力扣 | 子数组滑动窗口 | 560. 和为 K 的子数组、209. 长度最小的子数组、862. 和至少为 K的最短子数组、220. 存在重复元素 III

news2024/9/20 13:18:51

文章目录

  • 一、非滑动窗口
    • 1.1 560/LCR 010. 和为 K 的子数组
    • 1.2 862. 和至少为 K 的最短子数组
  • 二、滑动窗口
    • 2.1 209/LCR 008. 长度最小的子数组
    • 2.2 220. 存在重复元素 III

下面的题并不是全都由滑动窗口解决,有的题可以,有的题不可以,放入滑动窗口栏目的原因是,这些容易混淆。

一、非滑动窗口

当数组元素有负数时,不能使用滑动窗口,因为右指针右移可能使答案减小,也可能增大,左指针的移动同理,无法有效进行移动。

1.1 560/LCR 010. 和为 K 的子数组

LeetCode:LCR 010. 和为 K 的子数组
在这里插入图片描述
由于它需要找到的时连续子数组和为k的个数,也就是说看连续子数组中有多少个区间和刚好为k。我们观察到这里的nums[i]存在负数,因此使用双指针滑动窗口不太好判断左右指针的移动方向,因为右指针右移可能使答案减小,也可能增大,左指针的移动同理。

因此我们可以尝试使用前缀和,那么只需要找到两个数,其差为k即可,这使用哈希表就能解决。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        int sum = 0;
        int ans = 0;
        mp[0] = 1;
        
        for(int i = 0; i < nums.size(); ++ i){
            sum += nums[i];
            if(mp.count(sum - k)) ans += mp[sum - k];
            mp[sum] ++;
        }

        return ans;
    }
};

需要注意的是,需要初始化mp[0] = 1,我们要理解前缀和sum[i] - sum[j]表示的和的范围,范围为[j+1,i];那么我们要能够表示[0,i]那就必须出现sum[i] - sum[-1],实际上这里就是用sum[-1]=0来表示没有任何元素的时候的和。如果不加入这个和为0的值,则可能缺失答案。

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

LeetCode: 862. 和至少为 K 的最短子数组
在这里插入图片描述
本题和LeetCode:LCR 008. 长度最小的子数组不一样之处在于,本题存在负数,一旦存在负数,滑动窗口就不好使了。
本题和LeetCode:LCR 010. 和为 K 的子数组的区别有两点,第一点是这里求最小,第二点是这里和是至少为k

求最小我们自然而然可以想到前缀和时同一个数最右边那个效益最大,和至少为k导致不能使用哈希表,且不能使用二分查找,因为存在负数,前缀和有正有负。
那怎么办呢?哈希表和二分查找都不能用。 但是前缀和是要用的,毕竟区间问题转化成了两点问题。转化成前缀和后通过分析,我们有这样一个信息,同一个数越右边的数越有效;右边越小的数比左边更大的数有效,因为sum[i] - sum[j]sum[j]越小,这个差值越大,即区间和越大,而且右边的小数区间也更短,也就是说现在有两个信息了:
(1)使用前缀和
(2)右边的小数比左边的更大数或相等数 是更有效的,因为此时既更能满足条件,区间又更短

因此这个题的数具有“时效性”,我们可以考虑使用单调队列,我们遇到比之前的某些数更小的数一定是更有意义,因此放入队列,构成一个单调递增队列,不断弹出队尾元素,直到没有比它更小或等于的。我们保留比它小的原因是,可能有的数用刚加入的它不能满足条件,但是用比它更小的但是出现在之前的能满足条件。不过我们通过引入单调队列必然就使得每次加入都是有效的数,无效的都被剔除了。而且单调队列维护的是单增队列,可以使用二分查找找到属于它的合法值。

前缀和+单调队列+二分查找:
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

class Solution {
public:
    int shortestSubarray(vector<int>& nums, int k) {
        vector<long long> deque;//单增
        vector<int> place;
        deque.push_back(0);
        place.push_back(-1);
        long long sum = 0;
        int len = 0x3f3f3f3f;

        for(int i = 0; i < (int) nums.size(); ++ i){
            sum += nums[i];
            //查找是否有满足条件的值
            long long target = sum - k;// sum - target >= k 则sum - k >= target,找小于等于target的数
            auto it = upper_bound(deque.begin(), deque.end(), target);
            if(it != deque.begin()){//找到合适位置,当为begin时,没有小于等于target的
                -- it;
                len = min(len, i - place[it - deque.begin()]);
            }

            while(!deque.empty() && sum <= deque.back()){
                deque.pop_back();
                place.pop_back();
            }
            deque.push_back(sum);
            place.push_back(i);
        }

        return len == 0x3f3f3f3f ? -1 : len;
    }
};

我们可以进一步优化时间复杂度:
前缀和 + 单调双端队列:
时间复杂度: O ( n ) O(n) O(n)
在这里插入图片描述
这里引入了一个新的问题,也就是说我们在考虑sum[i]时,对于在单调队列中的元素sum[j]来说,一旦存在sum[i] - sum[j] >= target,那么sum[j]的任务就完成了就可以弹栈了,为什么呢?因为我们找到了[j+1,i]满足要求,对以j+1为左端点的区间,i是第一个满足条件的,i之后位置再有[j+1,i+x]满足题设条件时,区间长度已经不及[j+1,i]了,也就是说一旦出现一个i使得单调队列中元素满足条件,则这个元素可以功成名退。
因此我们可以直接考虑单调队列的队首,队首一旦满足条件就都能进行出队,因此进队出队一次,时间复杂度为 O ( n ) O(n) O(n)

class Solution {
public:
    int shortestSubarray(vector<int>& nums, int k) {
        deque<long long> deq;//单增
        deque<int> place;
        deq.push_back(0);
        place.push_back(-1);

        long long sum = 0;
        int len = 0x3f3f3f3f;

        for(int i = 0; i < (int) nums.size(); ++ i){
            sum += nums[i];
            //查找是否有满足条件的值
            while(!deq.empty() && sum - deq.front() >= k){
                len = min(len, i - place[0]);
                deq.pop_front();
                place.pop_front();
            }

            while(!deq.empty() && sum <= deq.back()){
                deq.pop_back();
                place.pop_back();
            }
            deq.push_back(sum);
            place.push_back(i);
        }

        return len == 0x3f3f3f3f ? -1 : len;
    }
};

二、滑动窗口

2.1 209/LCR 008. 长度最小的子数组

LeetCode:LCR 008. 长度最小的子数组
在这里插入图片描述
这个题可以使用滑动窗口,主要原因是,数组中全为正数,这样一来,当右指针右移时,连续子数组的和增加,左指针右移时,连续子数组的和减小。

为了找到所有可能满足条件的子数组,我们需要对滑动窗口不断伸缩,对于一个已经满足要求的滑动窗口而言,其右指针不动的情况下不能再找到满足条件的最小子数组,因为都为正数,右指针不变窗口变小不满足和大于等于target的要求,右指针不能左移因为你是通过右移得到这个位置的,左移肯定不行;右指针右移不满足要求,因为值变大但长度也变大。此时可能满足要求的情况还有,左指针右移到某个位置,以该位置为起点的滑动窗口。

那么我们左指针从0开始,右指针右移一直到和≥target或到数组尾部,如果和≥target的话,记录长度,然后左指针右移前往寻找下一个可能满足条件的滑动窗口左端点,一旦右移到<target则为可能的答案。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0;
        int right = 0;
        int slidewindow = nums[0];
        int len = 0x3f3f3f3f;

        while(right < nums.size()){
            if(slidewindow < target){
                right ++;
                if(right < nums.size()) slidewindow += nums[right];
            }
            if(slidewindow >= target){
                len = min(len, right - left + 1);
                if(right == left) break;//滑动窗口最短情况的临界条件
                slidewindow -= nums[left ++];
            }
        }
        return len == 0x3f3f3f3f ? 0 : len;
    }
};

当然本题也能使用前缀和+二分查找,由于本题要求的是大于等于而不是等于,因此不能用哈希表,但是由于前缀和恒增,所以可以通过二分查找来查找指定值cur_sum - target或小于它的第一个数

对比一下官解的解决方法:
官解保证循环每一次都更新一次右指针位置,相当于循环内部每次需要将左指针移动到正确位置,这里正确位置包括和还没达到要求,左指针不动,和达到要求,左指针往右移动到正确位置。注意这里保证右指针所到之处才加值。这样的话左指针大于右指针的时候和为0,一定下一次可以让右指针右移

而我的思路是循环内部只移动一次,可能左指针右移或右指针右移,具体情况看和的大小。

        int ans = INT_MAX;
        int start = 0, end = 0;
        int sum = 0;
        while (end < n) {
            sum += nums[end];
            while (sum >= s) {
                ans = min(ans, end - start + 1);
                sum -= nums[start];
                start++;
            }
            end++;
        }

2.2 220. 存在重复元素 III

LeetCode:220. 存在重复元素 III
在这里插入图片描述
本题有两种方法实现。

滑动窗口+哈希二分
在使用滑动窗口的方法中,我们维护一个indexDiff大小的滑动窗口,和209. 长度最小的子数组、862. 和至少为 K的最短子数组不同,由于这里没有区间大小求解限制,而且这里是两个元素之间的关系,因此元素存在负数没关系,对于新加入的每一个数,我们只需要找到满足条件的元素即可。

这里我们考虑是否能让滑动窗口里面的元素动态排序,然后还可以动态删除? 哈希表mapset能做到这一点,而且他们也能进行二分查找使用 O ( l o g n ) O(logn) O(logn)的时间找到所需要的答案。

class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int indexDiff, int valueDiff) {
        map<int, int> st;
        int left = 0, right = 0;

        while(right < (int) nums.size()){
            //right是当前要考虑的元素
            int target1 = nums[right] - valueDiff;
            int target2 = nums[right] + valueDiff;
            //查看有没有在 [target1,target2]内的元素

            //查看有没有数大于等于target1
            auto it = st.lower_bound(target1);
            if(it != st.end() && it->first <= target2) return true;
            //查看有没有数小于等于target2
            it = st.upper_bound(target2);
            if(it != st.begin()){
                it--;
                if(it->first >= target1) return true;
            }

            st[nums[right ++]] ++;
            if(right - left > indexDiff){
                if(-- st[nums[left]] == 0) st.erase(nums[left ++]);
            }
        }

        return false;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2058246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《黑神话:悟空》Steam峰值超200万 国内玩家占9成

《黑神话&#xff1a;悟空》自今天白天上午10点解锁以来&#xff0c;Steam在线峰值不断创新高&#xff0c;白天创下的140万记录又被晚上超过。据SteamDB统计&#xff0c;随着更多“打工人”回家休息&#xff0c;《黑神话&#xff1a;悟空》Steam同时在线已经突破了200万&#x…

编程之旅:从挫折到突破的心路历程

你是如何克服编程学习中的挫折感的&#xff1f; 编程学习之路上&#xff0c;挫折感就像一道道难以逾越的高墙&#xff0c;让许多人望而却步。然而&#xff0c;真正的编程高手都曾在这条路上跌倒过、迷茫过&#xff0c;却最终找到了突破的方法。你是如何在Bug的迷宫中找到出口的…

软件产品测试报告内容简析,第三方软件测试公司测试服务分享

在数字化快速发展的今天&#xff0c;软件产品的质量直接影响着企业的竞争力与市场表现&#xff0c;软件产品测试报告作为整个测试过程的总结性文档至关重要。 一、软件产品测试报告   软件产品测试报告是测试团队对软件系统的质量评估和诊断的重要文件&#xff0c;它包含了对…

LabVIEW滚动轴承故障诊断系统

滚动轴承是多种机械设备中的关键组件&#xff0c;其性能直接影响整个机械系统的稳定性和安全性。由于轴承在运行过程中可能会遇到多种复杂的工作条件和环境因素影响&#xff0c;这就需要一种高效、准确的故障诊断方法来确保机械系统的可靠运行。利用LabVIEW开发的故障诊断系统&…

VMwareWorkstation安装ESXi 7.0U3系统详细教程

版本信息 VMwareWorkstation版本如下&#xff1a; ESXI系统镜像版本如下&#xff1a; 安装步骤 ESXi虚拟机硬件配置 选择创建新的虚拟机 选择自定义&#xff0c;点击下一步 选择ESXi 7.0&#xff0c;点击下一步 选择稍后安装操作系统&#xff0c;点击下一步 按照图下所示选择…

十个方面100个网络安全相关知识点,快来学习!(上)

网络安全风险无处不在&#xff0c;现梳理了100个网络安全相关的小知识&#xff0c;希望能进一步提升大家的安全意识&#xff0c;帮助大家建立更加安全的网络环境。 一、账号密码安全 1. 如果有初始密码&#xff0c;应尽快修改。 2. 密码长度不少于8个字符。 3. 不要使用单一…

从零开始的nginx学习世界

一&#xff1a;nginx概述 1.1 nginx是什么 1&#xff1a;Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&…

Linux ubuntu 24.04 运行《文明5》游戏,解决游戏中文设置的问题!

Linux ubuntu 24.04 运行《文明5》游戏&#xff0c;解决游戏中文设置的问题&#xff01; 《文明5》是一款回合制经营策略游戏&#xff0c;拼的就是科技发展速度&#xff0c;点的是科技树&#xff0c;抢的就是科技制高点&#xff0c;但是真的是时间漫长&#xff0c;可能需要好几…

解密高可靠分布式锁:优化策略与技术实现指南

随着系统架构逐渐从单机走向分布式&#xff0c;如何在分布式环境下保证线程同步执行成为一个不可忽视的问题。分布式锁作为解决这一问题的关键技术&#xff0c;为分布式系统中的资源共享和任务协调提供了重要支持。选择合适的分布式锁实现方式&#xff0c;可以有效提高系统的可…

重塑“上海我店模式”的创新生态与绿色消费浪潮

在数字经济的浪潮中&#xff0c;一个名为“上海我店模式”的平台迅速崛起&#xff0c;短短三年内便实现了流水破百亿的壮举。该平台巧妙地将绿色消费积分机制融入本地生活与异业联盟商圈&#xff0c;不仅激活了庞大的私域流量&#xff0c;更以其独特的商业模式引领了消费新风尚…

hyper-v ubuntu下连接嵌入式linux板卡

用hyper-v非常的方便&#xff0c;不用装vm也不会那么臃肿&#xff0c;但如何在hyper-v和嵌入式板卡之间进行通讯呢&#xff1f; 1.环境 采用的是100ask-imx6ull板卡&#xff0c;hyper-v装的是ubuntu22系统。 hyper-v根据文章hyper-v上外网已经配置了一个虚拟网卡。 2.物理连…

vue2 proxy 代理配置报错 ValidationError: webpack Dev Server Invalid Options

vue/cli3&#xff08;本文实际使用的是vue/cli4&#xff09;代理配置如下&#xff1a; vue/cli4使用vue/cli3的代理配置会出现异常&#xff0c;本文解决办法为解决方法二处理&#xff0c;运行正常。 vue 启动项目 yarn serve 或者 npm run dev 或者 npm run serve 时&#x…

and design vue表格列宽度拖拽,vue-draggable-resizable插件使用

and design vue2版的table表格不能拖拽列的宽度&#xff0c;通过vue-draggable-resizable插件实现 我用的是and design 1.7.8的版本&#xff0c;先下插件 yarn add vue-draggable-resizable2.1.0我这版本的and design用最新3.0.0以上的插件会有问题&#xff0c;实现不了效果&a…

EmguCV学习笔记 VB.Net 5.3 透视变换

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

微分方程(Blanchard Differential Equations 4th)中文版Section2.2

动力系统的几何分析 捕食者-猎物系统的向量场 在第2.1节中&#xff0c;我们展示了两个不同捕食者-猎物系统的 R ( t ) R(t) R(t) 和 F ( t ) F(t) F(t) 图形&#xff0c;但没有描述我们是如何生成这些图形的。我们将在第2.5节中解决这个问题&#xff0c;采用欧拉方法推广到…

Java | Leetcode Java题解之第355题设计推特

题目&#xff1a; 题解&#xff1a; class Twitter {private class Node {// 哈希表存储关注人的 IdSet<Integer> followee;// 用链表存储 tweetIdLinkedList<Integer> tweet;Node() {followee new HashSet<Integer>();tweet new LinkedList<Integer&g…

《深入浅出WPF》读书笔记.6binding系统(上)

《深入浅出WPF》读书笔记.6binding系统(上) 背景 这一章是核心内容。databinding可以理解为实现逻辑层和展示层的桥梁。 binding基础 如何理解binding <Window x:Class"BindingSysDemo.BindingSysDemo1"xmlns"http://schemas.microsoft.com/winfx/2006/…

【pyqt5】QLineEdit中的文本输入限制方式,输入校验规则的应用详解

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

从0到1学会nginx分布式框架

nginx 项目地址晓智科技晓智科技晓智文档晓智文档源码地址源码地址文档源码文档源码 yum 安装 nginx 安装 yum-utils sudo yum install -y yum-utils添加 yum 源文件 # 新建文件 vim /etc/yum.repos.d/nginx.repo # 添加文件源 [nginx-stable] namenginx stable repo ba…

点击展开详细说明网站html引导页源码

点击展开详细说明网站html引导页源码,源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 https://download.csdn.net/download/huayula/89…