算法记录- 数组和滑动窗口

文章目录

  • 前言
  • 1. 长度最小的子数组
    • 1. 题目
    • 2. 图解
    • 3. 代码
  • 2. 水果成篮
    • 1. 题目
    • 2. 思路
    • 3. 代码
  • 3. 最小覆盖子串
    • 1. 题目
    • 2. 思路
    • 3. 代码


前言

数组和滑动窗口。滑动窗口这块也是数组里面比较常用的一个技巧,通常用法就是在数组里面找一个子序列,下面就通过几道题来了解滑动窗口的用法。题目还是在这个链接里面。



1. 长度最小的子数组

1. 题目

LeedCode209
给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

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

示例 2:
输入:target = 4, nums = [1,4,4]
输出:1

示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105



2. 图解

这道题我们可以使用滑动窗口来解欸,滑动窗口使用一个L指针,一个R指针,维护一个窗口,下面来图解看看这道题。首先说一下思路,首先把窗口里面的值的总和叫做 sum, 目标值 = target, 当 sum < target 的时候,R指针往右扩充,L不变,窗口变大, sum 也在不断变大,当 sum >= target 的时候, L 往右移动并且 sum 减少,当 sum == target 的时候记录下来,然后等到 sum < target 的时候,再移动右指针。

1、初始状态
算法记录- 数组和滑动窗口_第1张图片


2、R 一直移动,移动到 L ~ R 范围内的数字总和 >= 7
算法记录- 数组和滑动窗口_第2张图片


3、L开始向右移动,每移动一次判断是否达到条件,并且判断 sum 是否 < target,如果在图中等于 target,那么记录下来 min = R-L+1, 长度
算法记录- 数组和滑动窗口_第3张图片


4、继续重复当 sum > target 的时候L++,R不变
算法记录- 数组和滑动窗口_第4张图片


5、操作 L++,并不断查找sum是否等于target,如果等于,那么记录下来 min = Math.min(min, R-L+1)
算法记录- 数组和滑动窗口_第5张图片


6、继续向右搜索
算法记录- 数组和滑动窗口_第6张图片

7、L++,然后继续比较获取最小的min
算法记录- 数组和滑动窗口_第7张图片


8、L++, 此时 sum = 3 < 7,R++,R越界,结束,返回结果 min = 2

9、总结全部图
算法记录- 数组和滑动窗口_第8张图片



3. 代码

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //滑动窗口
        int L = 0;
        int R = 0;
        int sum = 0;
        //min是返回的结果
        int min = Integer.MAX_VALUE;
        while(R < nums.length){
            //每一次R++,sum += nums[R]
            sum += nums[R];
            while(sum >= target){
                //当sum >= target的时候,L++, R不变
                min = Math.min(min, R-L+1);
                //L移动一次sum就减少一个
                sum -= nums[L];
                L++;
            }
            R++;
        }
        //如果min == MAX_VALUE,那么证明全程都没有等于target的子序列,返回0
        return min == Integer.MAX_VALUE ? 0 : min;
    }
}



2. 水果成篮

1. 题目

LeedCode904: 水果成篮
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
    给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。

提示:

  • 1 <= fruits.length <= 105
  • 0 <= fruits[i] < fruits.length



2. 思路

题目说人话就是在一个数组中找到两个数字的最大子序列,比如 1,2,3,2,2就是4,因为最大子序列是2和3这两个数,这两个数出现了四次2,3,2,2.又比如 [3,3,3,1,2,1,1,2,3,3,4],最大就是5,是1和2这两个数,出现了5次,1,1,2,3,3。

解题思路就是滑动窗口,R++的时候记录下来水果的种类的数量,如果大于2,那么L++,否则R++。有兴趣可以自己画图试一下,这里就不给出图了。下面注释写得很清楚了。



3. 代码

class Solution {
    /**
        题目的意思就是给你一个数组, 你要在里面找到只包含两个数的最大子序列
        比如 1,2,3,2,2就是4, 因为2,3,2,2只包含2和3, 且是最长的
     */
    public int totalFruit(int[] fruits) {
        //使用滑动窗口
        int L = 0;
        int R = 0;
        int count = 0;  //count记录现在L-R之间的水果种类有多少
        int ans = 2;    //结果, 初始设置为2,结果肯定>=2的
        int[] res = new int[fruits.length]; //记录水果种数的数组, 0 <= fruits[i] < fruits.length作为限制不用担心内存爆栈的问题
        int len = fruits.length;        
        if(len < 2){
            return len; //长度小于2的就直接返回长度就可以了
        }
        while(R < len){
            //对应下标++
            res[fruits[R]]++;
            //如果对应下标是1的话, count+1表示找到一种不同类型的水果
            if(res[fruits[R]] == 1){
                count++;
            }
            //判断此时count有没有超过2了, 如果超过了左指针就要往右移动
            while(count > 2){
                res[fruits[L]]--;
                if(res[fruits[L]] == 0){
                    count--;
                }
                L++;
            }
            ans = Math.max(ans, R-L+1);
            R++;
        }
        return ans;

    }
}



3. 最小覆盖子串

LeedCode76: 最小覆盖子串

1. 题目

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”

示例 2:
输入:s = “a”, t = “a”
输出:“a”

示例 3:
输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串

提示:

  • 1 <= s.length, t.length <= 105
  • s 和 t 由英文字母组成



2. 思路

使用 size 和 start 记录字符串的开始位置和长度,使用滑动窗口 L 和 R,首先遍历一次 t,用一个 int 数组 result 记录下来 t 的每个字符的数量。然后滑动窗口遍历 s ,每次 R++ 的时候 result 对应的字符–,然后检测,左边字符++,对应 result 下标 ++,每一次都判断最大值和最大长度。最终返回。



3. 代码

class Solution {
    public String minWindow(String s, String t) {
        //滑动窗口,字符数组
        if(s == null || s.length() == 0 || t == null || t.length() == 0){
            return "";
        }
        int[] need = new int[128];
        //记录t的字符数
        for(int i = 0; i < t.length(); i++){
            need[t.charAt(i)] ++;
        }
        //左边界,右边界,size窗口大小,count是需求的字符个数,start是最小覆盖串开始的index
        int L = 0, R = 0, size = Integer.MAX_VALUE, count = t.length(), start = 0;
        //滑动窗口
        /**
            思路:首先R往右等到包含了t的所有字符
                然后此时L往右移动到第一个包含t的字符串的位置, 然后往右移动一格
                最后R再次往右移动
        
         */
        while(R < s.length()){
            char c = s.charAt(R);
            if(need[c] > 0){
                //此时c是需要的字符
                count --;
            }
            need[c]--;  //如果死是不需要的字符,最后变成负数
            if(count == 0){ //count=0表示找到了所有t中的字符
                //1、往左开始遍历去除不是必要的字符
                while(L < R && need[s.charAt(L)] < 0){
                    need[s.charAt(L)] ++;   //释放空间,恢复++
                    L++;                    //左指针往右移动
                }
                //找到了第一个要去除的必要字符
                if(R -L + 1 < size){        //此时的窗口比原来的要小
                    size = R - L + 1;
                    start = L;          //start记录下标
                }
                //下面去除第一个必要字符
                need[s.charAt(L)] ++;
                L ++;
                count++;            //去除了一个, 就要后面R++的时候补回来
            }

            R++;
        }
        //最后判断如果是Integer.MAX_VALUE就证明了一直没有更新, 根本找不到
        return size == Integer.MAX_VALUE? "" : s.substring(start, start + size);
    }
}





如有错误,欢迎指出!!!!

你可能感兴趣的:(算法,java,开发语言,算法)