Sliding window,滑动窗口类型

介绍部分来自:https://www.zhihu.com/question/36738189/answer/908664455

滑动窗口类型的题目经常是用来执行数组或是链表上某个区间(窗口)上的操作。比如找最长的全为1的子数组长度。滑动窗口一般从第一个元素开始,一直往右边一个一个元素挪动。当然了,根据题目要求,我们可能有固定窗口大小的情况,也有窗口的大小变化的情况。

下面是一些我们用来判断我们可能需要上滑动窗口策略的方法

  • 这个问题的输入是一些线性结构:比如链表呀,数组啊,字符串啊之类的
  • 让你去求最长/最短子字符串或是某些特定的长度要求

以下来自:https://labuladong.gitbook.io/algo/di-ling-zhang-bi-du-xi-lie/hua-dong-chuang-kou-ji-qiao-jin-jie

滑动窗口算法的思路是这样

1、我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。

2、我们先不断地增加 right 指针扩大窗口 [left, right),直到窗口中的字符串符合要求(包含了 T 中的所有字符)。

3、此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right),直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。

4、重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。

这个思路其实也不难,**第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解,**也就是最短的覆盖子串。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动,这就是「滑动窗口」这个名字的来历。

该算法的大致逻辑如下:

int left = 0, right = 0;while (right < s.size()) {`// 增大窗口window.add(s[right]);right++;while (window needs shrink) {// 缩小窗口window.remove(s[left]);left++;}
}

算法框架:

/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {unordered_map<char, int> need, window;for (char c : t) need[c]++;int left = 0, right = 0;int valid = 0; while (right < s.size()) {// c 是将移入窗口的字符char c = s[right];// 右移窗口right++;// 进行窗口内数据的一系列更新.../*** debug 输出的位置 ***/printf("window: [%d, %d)\n", left, right);/********************/// 判断左侧窗口是否要收缩while (window needs shrink) {// d 是将移出窗口的字符char d = s[left];// 左移窗口left++;// 进行窗口内数据的一系列更新...}}
}

经典题目:

1、Maximum Sum Subarray of Size K (easy)

最大子数组之和为k

描述

给一个数组nums和目标值k,找到数组中最长的子数组,使其中的元素和为k。如果没有,则返回0。

数组之和保证在32位有符号整型数的范围内

样例

样例1

输入: nums = [1, -1, 5, -2, 3], k = 3
输出: 4
解释:
子数组[1, -1, 5, -2]的和为3,且长度最大

样例2

输入: nums = [-2, -1, 2, 1], k = 1
输出: 2
解释:
子数组[-1, 2]的和为1,且长度最大

前缀和技巧

https://zhuanlan.zhihu.com/p/107778275

一、什么是前缀和

前缀和的思路是这样的,对于一个给定的数组 nums,我们额外开辟一个前缀和数组进行预处理:

int n = nums.length;
// 前缀和数组
int[] preSum = new int[n + 1];
preSum[0] = 0;
for (int i = 0; i < n; i++)preSum[i + 1] = preSum[i] + nums[i];

这个前缀和数组 preSum 的含义也很好理解,preSum[i] 就是 nums[0..i-1] 的和。那么如果我们想求 nums[i..j] 的和,只需要一步操作 preSum[j+1]-preSum[i] 即可,而不需要重新去遍历数组了。

/*** 前缀和算法* put 与 putIfAbsent区别:** put在放入数据时,如果放入数据的key已经存在与Map中,最后放入的数据会覆盖之前存在的数据,而putIfAbsent在放入数据时,如果存在重复的key,那么* putIfAbsent不会放入值。* putIfAbsent   如果传入key对应的value已经存在,就返回存在的value,不进行替换。如果不存在,就添加key和value,返回null** @param nums nums = [1, -1, 5, -2, 3]* @param k k = 3* @return 输出: 4*/
public static int maxSubArrayLen1(int[] nums, int k) {// Write your code hereMap<Integer, Integer> map = new HashMap<>(); // sum -> id   前缀和数组为 hashmap 中的 key, id 为下标map.put(0, -1);     // 这里需要第一个元素为 0int maxLen = 0;for (int i = 0, sum = 0; i < nums.length; i++) {sum += nums[i];if (map.containsKey(sum - k)) {         // sum[i] - k = sum[j]  说明前缀和i - 前缀和j = kmaxLen = Math.max(maxLen, i - map.get(sum - k)); // i - j = 区间大小} else {map.putIfAbsent(sum, i);}}return maxLen;
}

2、Smallest Subarray with a given sum (easy)

3、Longest Substring with K Distinct Characters (medium)

最多有k个不同字符的最长子字符串

描述

给定字符串S,找到最多有k个不同字符的最长子串T

样例

样例 1:

输入: S = "eceba" 并且 k = 3
输出: 4
解释: T = "eceb"

样例 2:

输入: S = "WORLD" 并且 k = 4
输出: 4
解释: T = "WORL" 或 "ORLD"

算法:

/*** @param s: A string* @param k: An integer* @return: An integer*/
public static int lengthOfLongestSubstringKDistinct(String s, int k) {// write your code hereif (s.length() == 0 || k == 0)return 0;int res = 0;                    // 结果char[] ch = s.toCharArray();    // 字符数组// 窗口 [left, right)int left = 0;       // 左指针int right = 0;      // 右指针Map<Character, Integer> windowMap = new HashMap<>();    // 窗口 字符->出现次数while(right < s.length()){char c = ch[right];     // 将移入窗口的字符right++;                // 右移// 进行窗口内数据的一些列更新if (windowMap.containsKey(c)){windowMap.put(c, windowMap.get(c) + 1);      // 更新出现次数}else{windowMap.put(c, 1);}// 判断是否要收缩窗口while (windowMap.size() > k){char lc = ch[left];     // 要移除的字符left++;                 // 左窗口右移// 进行窗口内数据的一些列更新int count = windowMap.get(lc);if (count > 1) {windowMap.put(lc, windowMap.get(lc) - 1);}else {windowMap.remove(lc);}}// 更新答案res = Math.max(res, right - left);}return res;
}

4、Fruits into Baskets (medium)

904. 水果成篮

类似:928. 最多有两个不同字符的最长子串

描述

在一排树中,第 i 棵树产生 tree[i] 型的水果。
你可以从你选择的任何树开始,然后重复执行以下步骤:

把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
移动到当前树右侧的下一棵树。如果右边没有树,就停下来。
请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1,然后执行步骤 2,依此类推,直至停止。

你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。

用这个程序你能收集的水果树的最大总量是多少?

示例 :

示例 1 :

输入:[1,2,1]
输出:3
解释:我们可以收集 [1,2,1]。

示例 2:

输入:[0,1,2,2]
输出:3
解释:我们可以收集 [1,2,2]
如果我们从第一棵树开始,我们将只能收集到 [0, 1]。

示例 3:

输入:[1,2,3,2,2]
输出:4
解释:我们可以收集 [2,3,2,2]
如果我们从第一棵树开始,我们将只能收集到 [1, 2]。

示例 4:

输入:[3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:我们可以收集 [1,2,1,1,2]
如果我们从第一棵树或第八棵树开始,我们将只能收集到 4 棵水果树。
public static int totalFruit(int[] tree) {if (tree.length == 0)return 0;int res = 0;    // 结果int left = 0;   // 左指针int right = 0;  // 右指针Map<Integer, Integer> windowMap = new HashMap<>();      // 窗口while (right < tree.length){int ri = tree[right];       // 进入右窗口的值right++;                    // 右移// 进行窗口内数据的一系列更新windowMap.put(ri, windowMap.getOrDefault(ri, 0) + 1);// 判断是否需要左窗口移动while (windowMap.size() > 2){int li = tree[left];    // 移除窗口的值left++;                 // 左窗口右移// 进行窗口内数据的一系列更新int count = windowMap.get(li);if (count > 1){windowMap.put(li, windowMap.get(li) - 1);}else {windowMap.remove(li);}}// 结果更新res = Math.max(res, right - left);}return res;
}

5、No-repeat Substring (hard)

  1. 最长无重复字符的子串
描述

给定一个字符串,请找出其中无重复字符的最长子字符串。

样例

样例 1:

输入: "abcabcbb"
输出: 3
解释: 最长子串是 "abc".

样例 2:

输入: "bbbbb"
输出: 1
解释: 最长子串是 "b".
public class Solution {/*** @param s: a string* @return: an integer*/public int lengthOfLongestSubstring(String s) {// write your code hereif (s.length() == 0)return 0;int res = 0;char[] ch = s.toCharArray();int left = 0;int right = 0;Map<Character, Integer> windowMap = new HashMap<>();while (right < ch.length){char rc = ch[right];    // 放入窗口的值right++;                // 窗口右移// 进行窗口的数据的一系列操作windowMap.put(rc, windowMap.getOrDefault(rc, 0) + 1);// 判断左窗口是否要收缩while (windowMap.get(rc) > 1){char lc = ch[left];     // 从窗口移除的值left++;                 // 左窗口右移// 进行窗口的数据的一系列操作int count = windowMap.get(lc);if (count > 1){windowMap.put(lc, windowMap.get(lc) - 1);}else{windowMap.remove(lc);}}// 判断结果res = Math.max(res, right - left);}return res;}
}

6、Longest Substring with Same Letters after Replacement (hard)

最长重复字符置换

  1. 替换后的最长重复字符
描述

给定一个仅包含大写英文字母的字符串,您可以将字符串中的任何一个字母替换为的另一个字母,最多替换k次。 执行上述操作后,找到最长的,只含有同一字母的子字符串的长度。

字符串长度和k的大小不会超过10^4。

样例

样例1

输入:
"ABAB"
2
输出:
4
解释:
将两个'A’替换成两个’B’,反之亦然。

样例2

输入:
"AABABBA"
1
输出:
4
解释:
将中间的 'A’ 替换为 'B' 后得到 “AABBBBA"。
子字符串"BBBB" 含有最长的重复字符, 长度为4。

解题思路:来自:https://blog.csdn.net/qq_17550379/article/details/99309142

我们首先可以想到通过滑动窗口来做这个题目。我们首先有一个朴素的想法,就是窗口中的最多重复元素尽可能的多,基于此我们就需要遍历的过程中记录窗口里面出现的最多重复元素的个数。例如

A B A B B
↑   ↑
l   r
123

此时窗口中的最多重复元素A,并且它的个数是2。接着我们看一下移动窗口的过程中,如何去维护它。

我们假设k=1,那么此时需要替换的元素个数r-l+1-2=1,我们发现此时等于k,所以我们需要继续扩大窗口。

A B A B B
↑     ↑
l     r
123

此时B出现了2次,那么最多重复元素B(虽然BA一样多,但是我们要最近出现的那个),并且r-l+1-2>k,所以我们需要缩小窗口

A B A B B↑   ↑l   r
123

此时r-l+1-2=k,所以我们扩大窗口。

A B A B B↑     ↑l     r
123

我们发现此时的B出现了3次,并且r-l+1-3=k满足条件,我们需要将此时的窗口大小记录下来。

1、窗口大小 - 出现最多的字符 = 变换的次数k 符合条件

2、窗口大小 - 出现最多的字符 > 变换的次数k 需要缩小窗口

3、窗口大小 - 出现最多的字符 < 变换的次数k 需要扩大窗口

public class Solution {/*** @param s: a string* @param k: a integer* @return: return a integer*/public int characterReplacement(String s, int k) {// write your code hereif (s.length() == 0)return 0;int res = 0;char[] ch = s.toCharArray();int left = 0;   // 左窗口指针int right = 0;  // 右窗口指针Map<Character, Integer> windowMap = new HashMap<>();    // 窗口int maxCount = 0;       // 窗口中的匹配最多的字符的个数while (right < ch.length){char rc = ch[right];        // 进入右窗口的值right++;                    // 右移// 进行窗口内数据的一系列操作windowMap.put(rc, windowMap.getOrDefault(rc, 0) + 1);   // 加到窗口中maxCount = Math.max(maxCount, windowMap.get(rc));                    // 更新最多的字符的个数// 判断是否需要缩小窗口 (这里是窗口中其他个数的字符大于 k 就缩小,以为可以替换 k 次)while (right - left - maxCount > k){char lc = ch[left];     // 移除窗口的值left++;                 // 左窗口右移// 进行窗口内数据的一系列操作int count = windowMap.get(lc);if (count > 0){windowMap.put(lc, count - 1);}else {windowMap.remove(lc);}}// 更新最多的结果res = Math.max(res, right - left);}return res;}
}

7、Longest Subarray with Ones after Replacement (hard)

1493. 删掉一个元素以后全为 1 的最长子数组

滑动窗口类型(Sliding window)相关推荐

  1. Java算法-滑动窗口算法(Sliding Window)(十)

    滑动窗口 滑动窗口概念不仅存在于数据链路层,也存在于传输层,两者有不同的协议,但基本原理是相近的.其中一个重要区别是,一个是针对于帧的传送,另一个是字节数据的传送. 滑动窗口(Sliding wind ...

  2. LeetCode 滑动窗口(Sliding Window)类问题总结

    导语 滑动窗口类问题是面试当中的高频题,问题本身其实并不复杂,但是实现起来细节思考非常的多,想着想着可能因为变量变化,指针移动等等问题,导致程序反复删来改去,有思路,但是程序写不出是这类问题最大的障碍 ...

  3. python实现滑动窗口平均_数据流滑动窗口平均值 · sliding window average from data stream...

    [抄题]: 给出一串整数流和窗口大小,计算滑动窗口中所有整数的平均值. MovingAverage m = new MovingAverage(3); m.next(1) = 1 // 返回 1.00 ...

  4. [Swift]LeetCode480. 滑动窗口中位数 | Sliding Window Median

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblog ...

  5. TCP滑动窗口(Sliding Window)原理

    ---------------- 版权声明:本文为CSDN博主「CQ小子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csd ...

  6. uiautomation遍历windows所有窗口_万字长文!滑动窗口看这篇就够了!

    大家好,我是小浩.今天是小浩算法 "365刷题计划" 滑动窗口系列 - 整合篇.之前给大家讲解过一些滑动窗口的题目,但未作系统整理. 所以我就出了这个整合合集,整合工作中除了保留原 ...

  7. TCP 滑动窗口协议 详解

    滑动窗口机制 (1).窗口机制     滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口:同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口. ...

  8. TCP协议中的核心知识点,SYN Flood?ISN?滑动窗口?数据重传?拆包粘包?单tcp连接多请求?拥塞管理?(个人收藏学习笔记)

    TCP协议中的核心知识点,滑动窗口?数据重传?拆包粘包?单tcp连接多请求? 1.前言 2.TCP/IP四层结构 3. TCP 3.1 TCP 协议头 3.2 TCP通信过程 3.2.1 建立连接的三 ...

  9. TCP滑动窗口(发送窗口和接受窗口)

    TCP窗口机制 TCP header中有一个Window Size字段,它其实是指接收端的窗口,即接收窗口.用来告知发送端自己所能接收的数据量,从而达到一部分流控的目的. 其实TCP在整个发送过程中, ...

最新文章

  1. 接口限流算法:漏桶算法令牌桶算法
  2. 【字符串】最长回文子串 ( 动态规划算法 ) ★
  3. python环境设置_MacOS中的Python(和NumPy)开发环境设置
  4. wxWidgets:wxSingleChoiceDialog类用法
  5. java面试常考_JAVA面试常考系列十
  6. linux学习之lvm-逻辑卷管理器
  7. .NET MVC+ EF+调用存储过程 多表联查以及VIEW列表显示
  8. Bootstrap使用 .dropdown-menu 类创建下拉菜单
  9. idea打开vue项目后报错ESLint: Expected space or tab after ‘//‘ in comment.(spaced-comment)
  10. 3.1 神经网络概览
  11. 设计模式--spring源码中使用策略模式(Strategy Pattern)
  12. Java中的集合HashSet、LinkedHashSet、TreeSet和EnumSet(二)
  13. 二元一次方程用计算机怎么解,二元一次方程的解法
  14. lua utf8 gbk 编码转换
  15. NSGA_2 Matlab带约束问题的多目标优化求解方案+惩罚函数
  16. Science子刊:利用DTI和NODDI纵向研究揭示轻度脑外伤后的白质微结构改变
  17. 2021年CSDN年度总结:生活总是焦虑与希望并存,流水要争先,靠的是绵绵不绝。
  18. 英特尔最新超级计算机,全球超级计算机500强三分之二使用英特尔的处理器
  19. 计算机房屋出租系统毕业设计选题意义的说明
  20. 快速安装到安卓手机软件

热门文章

  1. 阿里云服务器安装配置
  2. 《辛雷学习方法》读书笔记——第一章 总论
  3. JS操作表格样式汇总
  4. 八百客、销售易、纷享销客各行其道
  5. jzxx1783小高考
  6. java fx scene builder_JavaFX开发工具之JavaFX Scene Builder
  7. 设备管理系统——c语言实现
  8. Nuke关于Grade节点的一些笔记
  9. 静态网页制作教程 (转载)
  10. 机器学习实战教程(一):K-近邻算法---电影类别分类