【java算法专场】滑动窗口(上)

目录

滑动窗口

基本概念

长度最小的子数组

 算法分析

算法步骤

示例

算法代码

无重复字符的最长子串

算法分析

算法步骤

示例

算法代码

最大连续1的个数 III

算法分析

算法步骤

示例

算法代码

  将 x 减到 0 的最小操作数

算法分析

算法步骤

示例

算法代码


滑动窗口

基本概念

滑动窗口(同向双指针)是一种基于双指针的一种思想,两个指针之间构成了一个窗口。

类型:窗口一般分为两类:一、固定大小的窗口;二、大小动态变化的窗口

适用场景

  • 给的数据结构是数组/字符串;
  • 求子数组/字符子串的最长最短等最值问题或者是在某个目标值时;
  • 问题本身是可以通过暴力求解。

下面通过一些题目,来加深对滑动窗口的理解。

长度最小的子数组

 算法分析

这道题其实就是要找一个连续的最短子数组,且这个子数组中所有元素之和要大于等于目标值target。如果使用暴力枚举每一个子数组的话,时间复杂度就会达到O(N^2),这道题的话,有更好的方法,那就是使用滑动窗口,接下来就解析一下如何使用滑动窗口来解这道题。

算法步骤

  1. 初始化双指针:设置两个指针leftright并初始化为0,表示窗口的左右边界。设置一个sum并初始化为0,用于计算窗口中所有元素之和。设置min并初始化为Integer.MAX_VALUE,用于记录符合目标的最小连续数组长度。
  2. 右边界(right)移动:让right往右移动,并nums[right]加入到sum中,这个过程相当于在扩大窗口的大小,让sum的值不断接近target。
  3. 更新min值:当sum的值大于等于target之后,利用min方法,更新min的值。
  4. 左边界(right)右移:当更新完min值之后,需要让left往右移,并且让sum减去nums[left]的值。这个过程相当于在缩小窗口。当sum的值比target小时,停止右移。
  5. 重复2-3-4操作:当走完步骤4之后,继续移动右边界,循环上述操作,直到right>nums.length。
  6. 返回min值:在返回min时,需要注意一下,可能数组中的所有元素相加都不能大于等于target,这时min依然是初始值,所以在返回时,需要判断一下min值。

时间复杂度O(n),其中n为数组的长度,每个元素最多被遍历两次,第一次是扩大右边界时,第二次是左边界右移时。

空间复杂度:O(1),在算法中,只用了常数个变量。

示例

这里我们以第一个例子为例{2,3,1,4,3},target=7

第一步:初始值

 第二步:右边界右移

第三步:更新min值

此时min值为4

第四步:左边界右移

第五步:重复2-3-4步,直到遍历完整个数组

第六步:返回min值 【4,3】

算法代码

class Solution {public int minSubArrayLen(int target, int[] nums) {int left=0;int right=0;int sum=0;int min=Integer.MAX_VALUE;for(;right<nums.length;){//将窗口中的元素添加到sum中sum+=nums[right];//判断sum是否大于等于targetwhile(sum>=target){//将num[left]的值从sum从sum中减去sum-=nums[left];//更新最小长度min=Math.min(min,right-left+1);//左边界右移left++;}//扩大右边界right++;}//判断是否min大小,若是Integer.MAX_VALUE则返回0return min==Integer.MAX_VALUE?0:min;}
}

无重复字符的最长子串

算法分析

 题目要求找不重复的最长子串的长度,如果直接暴力枚举出每一个子串,时间复杂度会达到O(n^2),这样的方法,可能会超时,所以,这道题我们可以使用滑动窗口还有哈希表来解决。

算法步骤

  1. 初始化变量:定义双指针left和right,并初始化为0。设置一个hash数组,用来模拟哈希表,【hash数组的大小根据ASCII码表,一共有128个字符,所以我们可以设置为128】。定义ans用来存储最长不重复子串的长度,并初始化为0.
  2. 右边界(right)移动:将right往右移动,在移动的过程中,每移动一次,就将ch[right]在hash中以ch[right]为下标的元素加1。
  3. 更新ans值:在右边界移动的过程中,每次都将ans的值进行更新。
  4. 处理重复字符:当right移动到某个位置之后,要将ch[right]在hash对应的位置元素+1,需要先判断当前位置元素是否大于1,若大于1,说明子串遇到了重复的字符,此时需要移动left指针,并将在hash中以ch[left]为下标的元素-1,同时left右移,直到将重复元素排除。
  5. 遍历结束:当right遍历完字符串,结束循环,并返回ans。

时间复杂度:O(n),n是字符串长度,在入窗口的时候,right遍历一遍,left在最坏情况下也只遍历一遍。

空间复杂度:O(1),用的都是固定大小的变量。

示例

以{“abcabcbb”}为例

第一步:初始化

  • int left=0,right=0
  • hash[] = new int[128];
  • int ans=0;
  • char ch={'a','b','c','a','b','c','b','b'};

第二、三步:右边界right右移,并且在right往右走的过程中,更新ans的值

 第四步:处理重复字符

在上图中,第5个图,就是进行去重的情况。

第五步:重复上述操作,直到right到字符串末尾

算法代码

 /*** 计算给定字符串中最长无重复字符的子串的长度。* 通过维护一个滑动窗口,使用数组hash来记录窗口内每个字符出现的次数。* 当遇到重复字符时,缩小窗口的左边界,直到重复的字符不再出现在窗口内。* 在每次移动窗口右边界时,更新最长无重复子串的长度。** @param s 输入的字符串* @return 最长无重复字符子串的长度*/public int lengthOfLongestSubstring(String s) {// 初始化最长长度为0int ans = 0;// 初始化窗口的左右边界int left = 0;int right = 0;// 将字符串转换为字符数组,方便操作char[] ch = s.toCharArray();// 创建一个数组作为简化版的哈希表,用于记录字符出现的次数int[] hash = new int[128];// 遍历字符数组for (; right < ch.length; right++) {// 将当前字符的出现次数加1hash[ch[right]]++;// 当当前字符出现次数大于1时,说明遇到了重复字符while (hash[ch[right]] > 1) {// 将窗口左边界右移,并将左边界字符的出现次数减1hash[ch[left++]]--;}// 更新最长无重复字符子串的长度// 更新ansans = Math.max(ans, right - left + 1);}// 返回最长无重复字符子串的长度return ans;}

最大连续1的个数 III

算法分析

这道题给定了一个只有0和1的数组,且给定允许改变数组中k个0,通过改变数组中k个0来得到一个最长连续1。如果我们使用暴力枚举的话,时间复杂度会达到O(n^2),可能会超时。我们可以使用滑动窗口以及给其外加一个计零器来解决。

算法步骤

  1. 初始化:定义两个指针leftright并初始化为0。设置一个zero作为计零器并初始化为0.设置一个ans并初始化为0,用来记录最长1的长度。
  2. 扩大窗口:右边界right往右移,并判断nums[right]是不是0,若是0,则zero++。
  3. 左边界右移:当zero大于k时,说明此时在窗口内的0大于k,要进行出窗口,直到zero=k。
  4. 更新ans:在right往右移的过程中,每次都需要更新ans值。
  5. 循环操作重复2-3-4操作
  6. 遍历结束:直到right走到数组末尾,结束遍历,返回ans。

时间复杂度:0(n),n是数组长度,在整个过程中,进窗口的时候,right遍历一遍数组,出窗口的时候,最坏的情况下,left遍历一遍数组。

空间复杂度:0(1),在算法中,只用到了几个变量。

示例

[1,1,1,0,0,0,1,1,1,1,0],K=2为例,

第一步:初始化

  • left=0,right=0;
  • zero=0,ans=0;

第二步:右边界移动

让right往右移

第三步:左边界右移,直到zero=k

 

第四步:重复上述操作

第五步:当right遍历完数组,此时返回结果即可。由图可知,最长连续1的个数为6.

算法代码

class Solution {/*** 在给定数组中找到最长的子数组,该子数组中的0和1的个数之和不超过k。* 该方法通过滑动窗口算法实现,避免了重复计算,提高了效率。* * @param nums 输入的整数数组,其中包含0和1。* @param k 允许的0和1的个数之和的最大值。* @return 返回满足条件的最长子数组的长度。*/
public int longestOnes(int[] nums, int k) {/* 初始化最长子数组的长度为0 */int ans = 0;/* 初始化滑动窗口的左边界 */int left = 0;/* 初始化滑动窗口的右边界 */int right = 0;/* 初始化窗口中0的个数 */int zero = 0;/* 遍历数组,移动右边界 */for (; right < nums.length; right++) {/* 如果当前元素为0,增加窗口中0的个数 */if (nums[right] == 0) {zero++;/* 如果窗口中0的个数超过了k,移动左边界,减少窗口中0的个数 */while (zero > k) {if (nums[left++] == 0) {zero--;}}}/* 更新最长子数组的长度 *///更新ans值ans = Math.max(ans, right - left + 1);}/* 返回最长子数组的长度 */return ans;
}
}

 将 x 减到 0 的最小操作数

算法分析

这道题如果我们按着它所给题意来做的话,是很困难的。题意就是想要让我们在数组两边进行操作,然后找两侧的数与x抵消之后让x为0。所以,我们可以采用正难则反的思想,既然是要求两侧的数之和能与x抵消,那么我们可以想象成就是要就是要在数组中找除了x之外,其他数之和的长度。最后让数组长度(len)减去其他数之和的长度,最终得到的就是我们想要能让x减为0的长度。

算法步骤

  1. 初始化:定义两个指针leftright,表示滑动窗口的两个边界。定义sum并初始化为0,用于记录滑动窗口中元素之和;定义target并初始化为0,用于后续记录数组中除了x之外其他数之和;定义ans并初始化为-1,用于数组中是否存在能让x为0的数,若没有则返回-1。
  2. 判断target:先将数组中所有的数加到target中,再让target减去x,并且判断此时target是否大于0,若小于0,说明数组中所有数之和比x要小,则直接返回-1.
  3. 扩大窗口:让right往右移动,并将nums[right]添加到sum中。
  4. 判断sum与target:若sum的值比target要大,说明此时需要缩小窗口,让sum减去nums[left]的值,并让left往右移动,直到sum不再大于target。
  5. 更新ans值:当sum的值与target相等,说明找到了一个符合条件的子数组,比较ans和right-left+1的大小,存放最大值在ans中。
  6. 重复:重复3、4、5操作,直到right走到数组末尾位置。
  7. 返回ans值:在返回值时,需要进行判断ans的大小,若ans=-1,说明数组不存在能让x为0的操作数;反之,则返回nums.length-ans的长度。

时间复杂度:O(n),n为数组长度,在入窗口的时候,right遍历一遍数组,在出窗口的时候,最坏情况下遍历一遍数组。

空间复杂度:O(1),只用了几个变量。

示例

 以[1,1,4,2,3],x=5为例

第一、二步:初始化,并将数组中所有元素添加到target,最后让target-x,判断target大小

target=1+1+4+2+3=11

target-5=6

第三、四步:扩大窗口,判断target与sum的大小

让right往右移动,并将nums[right]添加到sum中

第五步:更新ans值

在上图中,当right走到以2为下表的数时,此时sum与target相等,更新ans值

ans=Math.max(ans,right-left+1).,此时ans=3

第六步:重复上述操作。

第七步:返回ans值,当right遍历完数组之后,返回ans=3. 

算法代码

/*** 计算最少操作次数,使得数组nums中的元素之和减去x后为0。* 操作包括将数组中的任意一个元素翻倍或减半。* * @param nums 输入的整数数组* @param x 需要减去的目标值* @return 返回最少操作次数,如果无法实现则返回-1*/
public int minOperations(int[] nums, int x) {// 初始化答案为-1,表示未找到符合条件的解int ans = -1;// 初始化左右指针和当前窗口内元素之和int left = 0;int right = 0;int sum = 0;// 计算目标值,即数组元素之和减去xint target = 0;for (int digit : nums) {target += digit;}target -= x;// 如果目标值小于0,说明无法通过操作达到目标,直接返回-1if (target < 0) return ans;// 遍历数组,寻找符合条件的最长子数组while (right < nums.length) {sum += nums[right];// 如果当前窗口内元素之和大于目标值,移动左指针缩小窗口while (sum > target) {sum -= nums[left++];}// 如果当前窗口内元素之和等于目标值,更新答案if (sum == target) {ans = Math.max(ans, right - left + 1);}right++;}// 根据答案计算并返回最终结果,如果答案为-1,则返回数组长度return ans == -1 ? -1 : nums.length - ans;
}

以上就是本篇所有内容,若有不足欢迎指正~

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

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

相关文章

location匹配的优先级和重定向

nginx的重定向&#xff08;rewrite&#xff09; location 匹配 location匹配的就是后面的uri /wordpress 192.168.233.10/wordpress location匹配的分类和优先级 1.精确匹配 location / 对字符串进行完全匹配&#xff0c;必须完全符合 2.正则匹配 ^-前缀级别&#xff…

[Vulnhub] Stapler wp-videos+ftp+smb+bash_history权限提升+SUID权限提升+Kernel权限提升

信息收集 IP AddressOpening Ports192.168.8.106TCP:21,22,53,80,123,137,138,139,666,3306, Using Nmap for scanning: $ nmap -p- 192.168.8.106 --min-rate 1000 -sC -sV The results are as follows: PORT STATE SERVICE VERSION 20/tcp closed ftp-data…

Java常用的三种注解

1、 基本注解 一、注解的概念 1、注解也叫元数据&#xff0c;是JDK1.5版本开始引入的一个特性&#xff0c;用于对代码进行说明&#xff0c;可以对包、类、接口、字段、方法参数、局部变量等进行注解。注解的形式是“单词”。 在不改变原有逻辑的情况下&#xff0c;使用注解在源…

白化(Whitening)的原理是什么?

**白化&#xff08;Whitening&#xff09;**是数据预处理中的一个重要步骤&#xff0c;其目的是将原始数据的各个维度之间的线性相关性去除&#xff0c;并使得每个维度的方差都为1。通过白化&#xff0c;可以简化后续的数据处理步骤&#xff0c;如独立成分分析&#xff08;ICA&…

【简历】兰州某大学一本硕士:面试通过率基本是为0

注&#xff1a;为保证用户信息安全&#xff0c;姓名和学校等信息已经进行同层次变更&#xff0c;内容部分细节也进行了部分隐藏 简历说明 这是一个一本硕士的Java简历&#xff0c;那这个简历因为学校本身&#xff0c;它是一个一本的硕士&#xff0c;我们一般认为这一本硕士&a…

如何增强人工智能的人岗匹配?

你玩过媒人游戏吗&#xff1f;这不是一件容易的事情。在家庭作业的世界里&#xff0c;匹配员工的技能通常是非常具有挑战性的。然而&#xff0c;技术在简化过程中起着至关重要的作用。利用人工智能构建技能本身将建立一个更准确、更动态的数据库&#xff0c;以促进未来人工智能…

Vxe UI vxe-table column 根据内容的长度来自适应列的宽度

Vxe UI vue vxe-table 根据内容的长度来自适应列的宽度 列的 width 宽度支持多种格式&#xff08;默认情况下是等比例分配&#xff09;&#xff1a; 固定像素&#xff1a;100 或者 ‘100px’ 百分比&#xff1a;‘20%’ 自适应内容&#xff1a;‘auto’ 代码 <template&g…

超声波清洗机哪个牌子好?耐用的超声波眼镜清洗机推荐

超声波清洗机大家也一定不陌生了&#xff0c;提起超声波清洗机啊&#xff0c;大家对他的印象一定是清洗眼镜&#xff0c;这话没毛病&#xff0c;但是&#xff0c;这仅仅只是清洗机的基本功&#xff0c;清洗机的功能远远比咱们想象中的强大&#xff0c;但是目前市面上的小型超声…

【第31章】MyBatis-Plus之注解配置

文章目录 前言一、注解介绍二、注解列表总结 前言 本文详细介绍了 MyBatisPlus 注解的用法及属性&#xff0c;提供了源码链接以便深入理解。欢迎通过下方链接查看注解类的源码。 Mybatis-Plus Annotation 源码 一、注解介绍 Mybatis-Plus注解统一存放在com.baomidou.mybatis…

【JavaScript 算法】动态规划:最优子结构与重叠子问题

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、最优子结构1.1 最优子结构的例子1.2 如何识别最优子结构 二、重叠子问题2.1 重叠子问题的例子2.2 解决重叠子问题的方法2.3 如何识别重叠子问题 三、经典动态规划问题及其 JavaScript 实现3.1 斐波那契数列3.2 背包问题…

GAMMA软件适配航天宏图一号多星干涉数据

文章目录 1.航天宏图一号 X-频段 多基雷达星座2.航天宏图算法人员小结3.双基成像与单基成像干涉处理区别 GAMMA软件是世界著名的瑞士GAMMA遥感公司开发的专门用于干涉雷达数据处理的全功能商业软件。作为业内标杆软件&#xff0c;被全球范围内的研究人员、公司和公共机构广泛使…

MS5199T芯片解决方案以及驱动程序(国产三路5VADC)

一、MS5199T芯片介绍 MS5198T/MS5199T 为适合高精度测量应用的低功耗、低噪 声、三通道差分输入的 16bit/24bit 模数转换器。其内部集成了低 噪声输入缓冲器、低噪声仪表放大器,当增益设置为 64,更新 速率为 4.17Hz 时,均方根噪声为 10nV。 MS5198T/MS5199T 还集 成了低端电…

使用F1C200S从零制作掌机之debian文件系统完善NES

一、模拟器源码 源码&#xff1a;https://files.cnblogs.com/files/twzy/arm-NES-linux-master.zip 二、文件系统 文件系统&#xff1a;debian bullseye 使用builtroot2018构建的文件系统&#xff0c;使用InfoNES模拟器存在bug&#xff0c;搞不定&#xff0c;所以放弃&…

Datawhale 2024 年 AI 夏令营第二期——电力需求预测挑战赛

#AI夏令营 #Datawhale #夏令营 1.赛事简介 随着全球经济的快速发展和城市化进程的加速&#xff0c;电力系统面临着越来越大的挑战。电力需求的准确预测对于电网的稳定运行、能源的有效管理以及可再生能源的整合至关重要。 2.赛事任务 给定多个房屋对应电力消耗历史N天的相关…

TCP协议:如何实现客户端和服务器端的交流?

实例&#xff1a;超简陋版人工AI对答程序 一、描述&#xff1a; 在客户端输入问题&#xff0c;服务器端将给出答案。 二、代码示例 1.客户端 步骤&#xff1a; 首先创建一个Scanner对象input&#xff0c;用于从控制台读取用户输入的问题。用户输入的一行文本将存储在quest…

【Jfrog Artifactory】配置邮件服务器

教程使用QQ邮箱 配置路径是&#xff1a; http://IP:8082/ui/admin/configuration/mail 进入到Mail Server&#xff0c;然后按照格式填入&#xff1a; Host &#xff1a;smtp.qq.com 【发送服务器】 Port&#xff1a;587 【我的环境465无法发送成功】 Username&#xff1a;QQ邮…

C++:从C语言过渡到C++

在这篇博客中&#xff0c;我将会介绍从C语言过渡到C的一些基础知识。 目录 C起源 C的关键字 输出hello&#xff0c;world ​编辑 命名空间 1.什么是命名空间 2.namespace的作用 3.域作用限定符 4.命名空间的使用 IO流 缺省参数 函数重载 引用 1.引用的定义 2.引…

【Python3】自动化测试_Playwright最简单示例

启动 Playwright实例&#xff1a;sync_playwright().start() 终止 Playwright 实例&#xff1a;myPlaywright.stop() Playwright 模块提供了一种启动浏览器实例的方法。以下是使用 Playwright 驱动自动化的典型示例&#xff1a; from playwright.sync_api import sync_playw…

C++入门基础简述

文章目录 前言1、C首个程序2、namespace关键字3、C输入/输出4、缺省参数5、函数重载6、C中的引用7、const 引用8、指针和引用的关系9、inline关键字10、nullptr关键字 前言 此篇文章主要简述流程&#xff1a;C首个程序 -> namespace关键字 -> C输入/输出 -> 缺省参数 …

【C++航海王:追寻罗杰的编程之路】一篇文章带你认识哈希

目录 1 -> unordered系列关联式容器 1.1 -> unordered_map 1.1.1 -> unordered_map的文档介绍 1.1.2 -> unordered_map的接口说明 1.2 -> unordered_set 2 -> 底层结构 2.1 -> 哈希概念 2.2 -> 哈希冲突 2.3 -> 哈希函数 2.4 -> 哈希冲…