算法:滑动窗口

article/2025/6/23 20:32:50

1.长度最小的子数组

209. 长度最小的子数组 - 力扣(LeetCode)

运用滑动窗口(同向双指针)来解决,因为这些数字全是正整数,在left位置确定的下,right++这个总sum会越大,所以我们先让nums[right]进窗口,然后判断sum是否大于等于target,如果大于等于就更新总长度len,再让left往右走。当right不满足条件时,就是找到最小的len。(这个算法的优点:利用单调性规避了很多不必要的遍历,时间复杂度O(2 * N))。

class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int n = nums.size();int sum = 0, len = n + 1;for(int left = 0, right = 0; right < n; right++){sum += nums[right];while(sum >= target){len = min(len, right - left + 1);sum -= nums[left++];}}return len == n + 1 ? 0 : len; }
};

2.无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣(LeetCode)

解决思路:滑动窗口,定义一个hash数组来映射对应的值

进窗口:将右指针对应的字符放入hash数组中。

判断:当右指针对应的字符已经超过一个了,就开始移动左指针,让左指针出窗口。

更新结果:取ret和[left,right]区间的最大值

时间复杂度:O(N)。空间复杂度:O(1)。

class Solution {
public:int lengthOfLongestSubstring(string s) {int n = s.size();int hash[128] = { 0 };int left = 0, right = 0;int ret = 0;while(right < n){hash[s[right]]++;//进窗口while(hash[s[right]] > 1)//判断{hash[s[left++]]--;//出窗口}ret = max(ret,right - left + 1);right++;}return ret;}
};

3.最大连续1的个数(三)

1004. 最大连续1的个数 III - 力扣(LeetCode)

思路:滑动窗口

1、定义左右指针left,right,0的计数器zero。

2、进窗口,nums[right]如果是1,无视,如果是0,计数器+1

3、判断zero的个数是否大于k,大于则出窗口(nums[left]如果是1无视,如果是0,zero--)

4、更新结果

class Solution {
public:int longestOnes(vector<int>& nums, int k) {int n = nums.size();int ret = 0;for(int left = 0, right = 0, zero = 0; right < n; right++){if(nums[right] == 0)//进窗口zero++;while(zero > k)//判断{if(nums[left++] == 0)//出窗口zero--;}ret = max(ret, right - left + 1);}return ret;}
};

4. 将x减到0的最小操作数

1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)

思路:如果直接考虑这个问题会很复杂,所以正难则反,直接考虑将中间段的数求和等于 target(总和 - x),最后再用总长度 - 中间段长度的最大值,就是要求的两端的最小值。这样思路就和第一道题一样。还是以滑动窗口的思路。

  1. 定义左右指针left,right
  2. 进窗口
  3. 如果滑动窗口中的总值 大于 target 出窗口
  4. 当窗口中的总值等于target时更新ret

class Solution 
{
public:int minOperations(vector<int>& nums, int x) {int n =nums.size();int sum = 0;for(int i = 0; i < n; i++){sum += nums[i];}int target = sum - x;if(target < 0)//数组中都是正整数,如果减出小于0的数,那么直接返回-1就行return -1;int count = 0,ret = -1;for(int left = 0, right = 0; right < n; right++){count += nums[right];//进窗口while(count > target)//判断{count -= nums[left++];//出窗口}if(count == target){ret = max(ret,right - left + 1);//更新结果,要ret的最大值}}return ret == -1 ? -1 : n - ret;}
};

5.水果成篮

904. 水果成篮 - 力扣(LeetCode)

解决思路: 滑动窗口+哈希

进窗口:将fruits的右指针指向的值放入hash中

判断:当hash中的值(种类)超过两个,就开始出窗口

出窗口:直接将hash中fruits的左指针指向的值--。然后判断这个值在hash中是否还存在,不存在就直接删除这个hash[friuts[left]]。

最后更新结果。

class Solution 
{
public:int totalFruit(vector<int>& fruits) {unordered_map<int ,int> hash;int n = fruits.size();int ret = 0;for(int left = 0, right = 0; right < n; right++){hash[fruits[right]]++;//进窗口while(hash.size() > 2)//判断{hash[fruits[left]]--;//出窗口if(hash[fruits[left]] == 0)hash.erase(fruits[left]);left++;}ret = max(ret, right - left + 1);//更新结果}return ret;}
};

因为这个代码大量使用了哈希的删除,会导致用时增加,题目中给了这个水果的种类不会超过100000种,所以我们也可以直接使用哈希数组来解决,再增加一个kinds来记录哈希数组中水果的种类。


class Solution 
{
public:int totalFruit(vector<int>& fruits) {int hash[100001] = { 0 };int n = fruits.size();int ret = 0;for(int left = 0, right = 0, kinds = 0; right < n; right++){if(hash[fruits[right]] == 0)kinds++;//用kinds来记录水果种类hash[fruits[right]]++;//进窗口while(kinds > 2)//判断{hash[fruits[left]]--;//出窗口if(hash[fruits[left]] == 0)kinds--;left++;}ret = max(ret, right - left + 1);}return ret;}
};

6.找到字符串中所以字母异位词

 438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

解决思路:滑动窗口,p子串的大小是固定的,相当于一个p长度大小的窗口在s中滑动,定义两个哈希数组,一个存储p中字符的出现次数,另一个存储窗口中字符的出现次数,再定义一个count来存储窗口中的有效字符。

进窗口:将right对应的字符放入hash2中,进入后如果是有效字符,并且数量小于等于p中hash的数量,就++count。

判断:当窗口大小大于p的长度就开始出窗口。

出窗口:先判断出窗口的值是否是有效的字符,是有效字符就count--,然后再出窗口。

最后当窗口中的有效字符等于p的长度,就是最终结果

class Solution 
{
public:vector<int> findAnagrams(string s, string p) {int hash1[26] = { 0 };int hash2[26] = { 0 };vector<int> ret;int m = p.size();for(auto e : p){hash1[e - 'a']++;//将p的值存入hash1中}//count存当前窗口有效字符的个数for(int left = 0, right = 0, count = 0; right < s.size(); right++){hash2[s[right] - 'a']++;//进窗口if(hash2[s[right] - 'a'] <= hash1[s[right] - 'a'])//当进窗口后的字符是有效字符,count++count++;if(right - left + 1 > m)//判断{if(hash2[s[left] - 'a'] <= hash1[s[left] - 'a'])//当出窗口前是有效字符count--count--;hash2[s[left] - 'a']--;//出窗口 left++;}if(count == m)ret.push_back(left);}return ret;}
};

 7.串联所有单词的子串

438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

思路:这个题的思路和上一道题思路一样,还是滑动窗口+哈希表,只是上个题是字符,这个题是字符串, 不同的点是

1.哈希表是unordered_map<string,int>。

2.指针移动的步长是words中单词的长度。

3.还需要增加滑动窗口的执行次数。

所以hash2得定义到循环中,每次执行不同的滑动窗口的时候要用到不同的hash2。

若 hash2 定义在循环外会导致的问题

  1. 数据污染
    当处理完偏移量 i=0 后,hash2 会保留窗口中的单词频次。当处理 i=1 时,这些残留数据会导致:

    • 单词频次统计错误(包含前一轮的数据)
    • count 计数器失效(累计了无效计数)
class Solution {
public:vector<int> findSubstring(string s, vector<string>& words) {unordered_map<string, int> hash1;//保存words里面所以单词的频次vector<int> ret;for(auto &e : words){hash1[e]++;} int len = words[0].size();int m = words.size();for(int i = 0; i < len; i++)//执行len次,因为words字符串出现的地方不一定是len整数倍的地方{unordered_map<string, int> hash2;//维护窗内单词的频次for(int left = i, right = i, count = 0; right + len <= s.size(); right += len){string in = s.substr(right, len);hash2[in]++;//进窗口if(hash2[in] <= hash1[in])count++;if(right - left + 1 > len * m)//判断{string out = s.substr(left, len);if(hash2[out] <= hash1[out])count--;hash2[out]--;//出窗口left += len;}if(count == m){ret.push_back(left);}}}return ret;}
};

8.最小覆盖子串

76. 最小覆盖子串 - 力扣(LeetCode)

思路:滑动窗口+哈希表

遍历t字符串,用kinds来记录t中的种类,用count来记录窗口中的有效种类,用begin和minlen来记录要返回的子串的开始位置和长度。

进窗口:还是直接将right所指向的值放入hash2中,因为count记录的是窗口中的有效种类,所以判断进窗口后hash1和hash2对应的值相对的话有效种类count++。

判断:当满足条件时出窗口。

更新结果:窗口区间小于minlen时就更新minlen和begin。

出窗口:出窗口前先判断hash1和hash2对应的值是否相等,相等的话就--count。再出窗口

class Solution {
public:string minWindow(string s, string t) {int kinds = 0;int hash1[128] = { 0 };int hash2[128] = { 0 };for(auto e : t){if(hash1[e] == 0)kinds++;hash1[e]++;}int begin = -1, minlen = INT_MAX;for(int left = 0, right = 0, count = 0; right < s.size(); right++){hash2[s[right]]++;if(hash2[s[right]] == hash1[s[right]])count++;//count记录窗口中有效的种类。while(count == kinds){if(right - left + 1 < minlen){minlen = right - left + 1;begin = left;}if(hash2[s[left]] == hash1[s[left]])count--;hash2[s[left]]--;left++;}}if(begin == -1)return "";elsereturn s.substr(begin, minlen);}
};


http://www.hkcw.cn/article/PJKGWFuVqy.shtml

相关文章

AI笔记 - 网络模型 - mobileNet

网络模型 mobileNet mobileNet V1网络结构深度可分离卷积空间可分![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/aff06377feac40b787cfc882be7c6e5d.png) 参考 mobileNet V1 网络结构 MobileNetV1可以理解为VGG中的标准卷积层换成深度可分离卷积 可分离卷积主要有…

新中地三维GIS开发智慧城市效果和应用场景

近年来&#xff0c;随着科技的发展和城市化进程的加速&#xff0c;智慧城市成为了全球各大城市的一个重要发展方向。 在这一背景下&#xff0c;三维GIS技术以其独特的优势&#xff0c;成为构建智慧城市不可或缺的工具。新中地GIS开发特训营正是在这样的大环境下应运而生&#…

Linux笔记---线程

1. 线程的介绍 1.1 线程的概念 基本定义&#xff1a; 线程&#xff08;Thread&#xff09;是操作系统能够进行运算调度的最小单位。它被包含在进程&#xff08;Process&#xff09;之中&#xff08;或者说是进程的一部分、对进程的划分&#xff09;&#xff0c;是进程中的实际…

Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)

前言&#xff1a;ArrayList是Java中最常用的动态数组实现之一&#xff0c;它提供了便捷的操作接口和灵活的扩展能力&#xff0c;使得在处理动态数据集合时非常方便。本文将深入探讨Java中ArrayList的实现原理、常用操作以及一些使用场景。 一&#xff1a;体系结构 二&#xff…

antddesign使用iconfont的字体库和图标库

antddesign使用iconfont 使用iconfont自定义字体 1️⃣选择一种需要的字体&#xff0c;点击【字体包下载】&#xff1a; 2️⃣下载好的字体放到项目目录下&#xff1a;src/assets/fonts&#xff1a; 3️⃣新建styles/font.css文件&#xff1a; /* src/styles/fonts.css */ f…

LearnOpenGL-笔记-其十二

今天我们来将LearnOpenGL的高级光照部分彻底完结&#xff1a; Bloom 泛光是一个非常常见的用于改善图像质量的手段&#xff0c;其主要做法就是将某个高亮度区域的亮度向四周发善以实现该区域更亮的视觉效果&#xff08;因为显示器的亮度范围有限&#xff0c;需要通过泛光来体…

第十二节:第一部分:集合框架:概述、Collection集合的常用方法

集合体系结构 Collection集合体系 Collection的常用方法 代码&#xff1a; 代码一&#xff1a;认识Collection体系的特点 package com.itheima.day17_Collection;import java.util.ArrayList; import java.util.HashSet;/* * 目标:认识Collection体系的特点。 * */ public cl…

C++哈希表:unordered系列容器详解

本节目标 1.unordered系列关联式容器 2.底层结构 3.模拟实现 4.哈希的应用 5.海量数据处理面试题 unordered系列关联式容器 在c98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可以达到logN&#xff0c;即最差的情况下需要比较红…

非常有趣的桌面萌宠互动软件

软件介绍 这里要介绍的软件是一款在主播直播领域十分实用的萌系插件&#xff0c;它能让主播的直播更具趣味性和吸引力。 软件开发者与特性 该软件由国外高中生kuroni开发&#xff0c;是一款开源软件。其最大的亮点在于&#xff0c;能让手鼓猫的手臂跟随鼠标和按键操作做出相…

InfluxQL 数据分析实战:聚合、过滤与关联查询全解析

InfluxQL 作为时序数据库的专用查询语言&#xff0c;在处理时间序列数据时展现出独特优势。本文深入探讨 聚合计算、数据过滤和跨测量关联 三大核心操作&#xff0c;通过真实代码示例展示如何从海量时序数据中提取关键洞察。文中涵盖从基础平均值计算到复杂多维度分析的完整流程…

记一次idea中lombok无法使用的解决方案

在注解处理器下&#xff0c;一般 Default 为“启用注解处理”和“从项目类路径获取处理器”&#xff0c;但是我的项目中的为选择“处理器路径”&#xff0c;导致了无法识别lombok&#xff0c;因此&#xff0c;需要改为使用“从项目类路径获取处理器”这个选项。如下图所示&…

【速通RAG实战:进阶】17、AI视频打点全攻略:从技术实现到媒体工作流提效的实战指南

一、AI视频打点的技术底层与数据处理流程 (一)视频内容结构化的核心技术栈 AI视频打点的本质是将非结构化视频数据转化为带时间戳的结构化信息,其技术流程涵盖音视频处理、语音识别、自然语言处理三大核心模块,形成“数据采集-内容解析-智能标记-协同应用”的完整闭环。 …

Java BigInteger类详解与应用

Java BigInteger类应用详解 BigInteger的构造方法&#xff1a; 对象一旦创建&#xff0c;内部的值不能发送改变 BigInteger常见的成员方法&#xff1a; 一、对象创建 BigInteger提供两种主要构造方式&#xff1a; // 通过字符串构造 BigInteger num1 new BigInteger("…

LLM优化技术——Paged Attention

在Transformer decoding的过程中&#xff0c;需要存储过去tokens的所有Keys和Values&#xff0c;以完成self attention的计算&#xff0c;称之为KV cache。 &#xff08;1&#xff09;KV cache的大小 可以计算存储KV cache所需的内存大小&#xff1a; batch * layers * kv-he…

Java并发编程实战 Day 1:Java并发编程基础与线程模型

【Java并发编程实战 Day 1】Java并发编程基础与线程模型 开篇&#xff1a;系列定位与学习目标 欢迎来到为期30天的《Java并发编程实战》系列教程。本系列将从Java并发编程的基础知识讲起&#xff0c;逐步深入到高级特性与实战应用&#xff0c;帮助开发者构建高性能、可扩展的…

如何配置国内docker镜像源?

如何配置国内docker镜像源&#xff1f; 安装docker chattr -i /etc/passwd chattr -i /etc/shadow chattr -i /etc/group chattr -i /etc/gshadow# 下载slirp4netns rpm包 wget https://1407433742.rsc.cdn77.org/c7-extras.x86_64/slirp4netns/20200428221211/0.4.3-4.el7_…

【Ubuntu】摸鱼技巧之虚拟机环境复制

前言 提示&#xff1a;所有的操作都需要关闭虚拟机 如何快速在其它电脑布置&#xff0c;linux环境&#xff0c;如果我们有一个环境直接拷贝就有时间摸鱼呀。 1.直接复制简单粗暴 不做赘述&#xff0c;如果不会复制&#xff0c;那么请右击鼠标压缩复制 2.克隆虚拟机 2.1 …

C51单片机

1.单片机的概述 (1)微处理器(CPU) 运算器主要负责数据的算术运算和逻辑运算。 控制器:是发布命令的“决策机构”&#xff0c;负责协调和指挥整个计算机系统操作。 (2)存储器 程序存储器:用于存储程序和一些固定不变的常数和表格数据&#xff0c;一般由只读存储器(ROM)组成。…

介绍一种LDPC码译码器

介绍比特翻转译码原理以及LDPC码译码器的设计。 1 译码理论 比特翻转&#xff08;BF&#xff09;译码算法是硬判决算法的一种。 主要译码思想是&#xff1a;当有一个校验矩阵出错时&#xff0c;BF 算法认为在这个校验矩阵中一定至少存在一个位置的码字信息是错误的&#xff1…

Python简易音乐播放器开发教程

&#x1f4da; 前言 编程基础第一期《12-30》–音乐播放器是日常生活中常用的应用程序&#xff0c;使用Python和pygame库可以轻松实现一个简易的音乐播放器。本教程将详细讲解如何开发一个具有基本功能的音乐播放器&#xff0c;并解析其中涉及的Python编程知识点。 &#x1f6e…