• 秋招必会的算法技巧--滑动窗口


    所谓的滑动窗口就是通过不断调整指针的起始位置和终止位置,从而得出想要的结果,我们先来看一道算法题

    Leetcode 209 长度最小的子数组 

     力扣题目链接

       给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

    示例:

    输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

       最直接的做法就是用双重循环,相当于内层循环每次都找出本次外循环变量位置之后的所有子数组,再比较其大小。 

     

    1. function minSubArray(s, nums) {
    2. let result = Infinity;
    3. let sum = 0;
    4. let subLength = 0;
    5. for (let i = 0; i < nums.length; i++) {
    6. sum = 0;
    7. for (let j = i; j < nums.length; j++) {
    8. sum += nums[j];
    9. if (sum >= s) {
    10. subLength = j - i + 1;
    11. result = Math.min(result, subLength);
    12. break;
    13. }
    14. }
    15. }
    16. return result === Infinity ? 0 : result;
    17. }

       这种解法的时间复杂度是O(n²),那么有没有一种办法用一次循环就遍历完整个数组,找到最小的数组长度呢,这种办法就是滑动窗口。

       双重循环时,我们用一个for表示起始位置,另一个表示终止位置,在滑动窗口中,我们想用一重循环就完成这个任务,那么循环变量就必须能表示整个范围,也就是必须是可以表示终止位置,先来看一下代码。

    1. function minSubArray(s, nums) {
    2. let result = Infinity;
    3. let sum = 0;
    4. let subLength = 0;
    5. let i = 0;
    6. for (let j = 0; j < nums.length; j++) {
    7. sum += nums[j];
    8. //当和满足条件时 缩小窗口 直到不满足条件为止
    9. while (sum >= s) {
    10. //计算子串长度
    11. subLength = j - i + 1;
    12. // 将更小的赋值给result
    13. result = Math.min(result, subLength);
    14. // 当缩小窗口时 总和是不断减小的
    15. // 这种滑动窗口也被称为最小滑窗
    16. sum -= nums[i];
    17. i++;
    18. }
    19. }
    20. return result === Infinity ? 0 : result;
    21. }

    这里引用一张代码随想录的滑窗图片。

    根据子序列情况,通过不断调整起始位置 ,就是滑动窗口的要点

    再看几道相关的题。 

    leetcode 76 最小覆盖子串 

     力扣题目链接

    1. var minWindow = function(s, t) {
    2. let left = 0 ,right = 0
    3. let substr = ''
    4. let res = ''
    5. let need = new Map()
    6. // 首先统计子串中的字母种类和数量 利用map统计
    7. for(let c of t){
    8. need.set(c,need.has(c)?need.get(c)+1:1)
    9. }
    10. // 需要的字母种类 就是map的长度
    11. let needType = need.size
    12. while(right < s.length){
    13. const c = s[right]
    14. // 在窗口扩大时 如果有子串需要的字母 就减少需要的量
    15. if(need.has(c)){
    16. need.set(c,need.get(c)-1)
    17. if(need.get(c)===0){
    18. needType-=1
    19. }
    20. }
    21. // 当子串需求都满足了 缩小窗口
    22. while(needType===0){
    23. //
    24. const c2 = s[left]
    25. substr = s.substring(left,right+1)
    26. if(!res||substr.lengthlength){
    27. res = substr
    28. }
    29. // 缩小窗口时如果是需要的字母 那么再增加需要的字母数量
    30. if(need.has(c2)){
    31. need.set(c2,need.get(c2)+1)
    32. if(need.get(c2)){
    33. needType+=1
    34. }
    35. }
    36. left++
    37. }
    38. right++
    39. }
    40. return res
    41. };

     leetcode 904 水果成篮

    1. var totalFruit = function(fruits) {
    2. let left = 0
    3. let right = 0
    4. let maxType = 2
    5. let fruitType = 0
    6. let fruitCnt = new Map()
    7. let maxLen
    8. while(right < fruits.length){
    9. const f = fruits[right]
    10. //如果是没加入的水果种类 种类数量增加
    11. if(!fruitCnt.get(f)) {
    12. fruitType+=1
    13. }
    14. //每一种水果的数量统计
    15. fruitCnt.set(f,fruitCnt.has(f)?fruitCnt.get(f)+1:1)
    16. // 当水果种类大于2 即不满足条件
    17. while(fruitType > maxType){
    18. const f2 = fruits[left]
    19. //缩小窗口的过程中 如果有已有的水果 那么水果数量减一
    20. if(fruitCnt.has(f2)){
    21. fruitCnt.set(f2,fruitCnt.get(f2)-1)
    22. }
    23. //如果数量减少完了 那就让水果种类减1
    24. if(fruitCnt.get(f2)===0){
    25. fruitType-=1
    26. }
    27. left++
    28. }
    29. // 在窗口扩大时 计算最大数目
    30. maxLen = Math.max(maxLen,(right - left)+1)
    31. right++
    32. }
    33. return right
    34. };

     

  • 相关阅读:
    免费玩云上大数据--海汼部落实验室
    【GNN Panel】图神经网络及其在结构建模中的应用
    JVM的运行时数据区
    期货交易如何定义趋势?
    常用归一化/正则化层:InstanceNorm1d、InstanceNorm2d、
    C++提高篇:深入理解纯虚函数和抽象类
    模板再认识
    golang的垃圾回收算法之十一Stack的处理
    爬虫 Edge浏览器安装Xpaht Helper插件平替Chrome浏览器Xpaht Helper插件定位元素
    黑客之批处理编写
  • 原文地址:https://blog.csdn.net/Ghost__H/article/details/126381867