数组之滑动窗口

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。

那么滑动窗口如何用一个for循环来完成这个操作呢。

首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。

如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?

此时难免再次陷入 暴力解法的怪圈。

所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。

最小滑窗模板

给定数组 nums,定义滑窗的左右边界 i, j,求满足某个条件的滑窗的最小长度。

while j < len(nums):
    判断[i, j]是否满足条件
    while 满足条件:
        不断更新结果(注意在while内更新!)
        i += 1 (最大程度的压缩i,使得滑窗尽可能的小)
    j += 1

 最大滑窗模板

给定数组 nums,定义滑窗的左右边界 i, j,求满足某个条件的滑窗的最大长度。

while j < len(nums):
    判断[i, j]是否满足条件
    while 不满足条件:
        i += 1 (最保守的压缩i,一旦满足条件了就退出压缩i的过程,使得滑窗尽可能的大)
    不断更新结果(注意在while外更新!)
    j += 1

   关键的区别在于,最大滑窗是在迭代右移右边界的过程中更新结果,而最小滑窗是在迭代右移左边界的过程中更新结果。因此虽然都是滑窗,但是两者的模板和对应的贪心思路并不一样,而真正理解后就可以在lc.76,lc.904,lc.3, lc.1004写出非常无脑的代码。

    时间复杂度为:O(N), 空间复杂度为:O(N).

其实双指针和滑动窗口是有些许区别的。滑动窗口一句话就是右指针先出发,左指针视情况追赶右指针。可类比男生暗恋女生,两人都在往前走,但男生总是默默跟着女生走但又不敢超过她。因此,右指针最多遍历一遍数组,左指针也最多遍历一次数组,时间复杂度不超过O(2N)。接下来,如何判断滑动窗口内是否满足题设条件,有两种选择:(1) 要么你遍历这个滑窗,通过遍历来断滑窗是否满足需要O(N), 那么总的时间就退化为O(N^2), (2) 要么你选择字典,用空间换时间,那么判断划窗是否满足条件则需要 O(1),总时间为O(N).

lc904 水果成篮(最大滑窗)

白话题意:求满足某个条件(数组值最多就两类的连续数组,例如[1,2,2,1,2])的最长数组长度

public int totalFruit(int[] fruits) {
        int n=fruits.length;
        Map cnt=new HashMap<>();
        int left=0,ans=0;
        for (int right = 0; right < n; right++) {
            cnt.put(fruits[right],cnt.getOrDefault(fruits[right],0)+1);
            while (cnt.size()>2){
                cnt.put(fruits[left],cnt.get(fruits[left])-1);
                if (cnt.get(fruits[left])==0){
                    cnt.remove(fruits[left]);
                }
                left++;
            }
            ans=Math.max(ans,right-left+1);
        }
        return ans;
    }

lc76 最小覆盖子串(最小滑窗)

public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen){
            return "";
        }

        char[] sCharArray = s.toCharArray();
        char[] tCharArray = t.toCharArray();

        // ascii('z') = 122,存字母对应的ASCII码
        int[] winFreq = new int[128]; //用来记录窗口内(也就是当前子串)的字符情况
        int[] tFreq = new int[128]; //用来记录t字符的频数
        for (char c : tCharArray){
            tFreq[c]++; //桶排序中统计出现次数的思想
        }

        //滑动窗口内部包含多少T中的字符,对应字符频数超过不重复计算
        int distance = 0; //用来记录t中字符在窗口中出现的频数
        int minLen = sLen + 1; //最小子串的长度,用于最后的substring
        int begin = 0; //开始下标,用于最后的substring

        //左右两个指针,也就是窗口的两个边界
        int left = 0;
        int right = 0;

        //左闭右开区间[left,right)
        while (right < sLen){ //当右指针没移动到头时
            //右指针向右移动逻辑
            char sCharRight = sCharArray[right]; //拿到当前右指针对应的字符
            if (tFreq[sCharRight] == 0){ //右指针的字符不是t中字符时
                right++; //向右移动
                continue;
            }

            if (winFreq[sCharRight]

 

你可能感兴趣的:(面试,算法,面试题,算法,面试,数据结构,java)