【算法与数据结构 の 代码实战】滑动窗口

滑动窗口法(尺取法):

  • 适用范围:在满足一定条件的「连续区间」进行查找;题目中基本都会出现「最小」、「最大」这样的字眼。

  • 思路:通过左右指针L和R构造一个滑动窗口,首先移动R使得滑动窗口使结果趋于目标,当超过目标范围时,再移动L使得结果回退趋向目标,如此循环往复,并在其过程中记录最优值

长度最小的子数组

在这里插入图片描述

分析

在这里插入图片描述
注意:
1.如果大于等于s的值出现在front()或back()或中间位置
2.如果nums的所有值加起来都小于s

class Solution {
    
    
public:
    int minSubArrayLen(int s, vector<int>& nums) {
    
    
        if(nums.empty() == true) return 0;
        //寻找最小值初始化为无穷大,
        //寻找最大值初始化为无穷小(可以取正数负数和0),
        //寻找最大值初始胡为0(只能取正数和0)
        int minn = 1 << 31 - 1; //符合s条件的最短长度
        int left = 0;
        int right = 0;
        //读取数组元素left的时候,
        //由于一开始不确定数组是否为空,故在初始化时必须先进行empty()判断
        int sum = nums[left]; //左右指针范围内求和
        //一开始我时打算写成
        //while(right<nums.size() || left<nums.size())但这样会使得过程复杂很多
        //分开两步写while(right<nums.size())和while(left<nums.size())即可使逻辑变得明朗
        while(right < nums.size()){
    
    
            if(sum >= s) {
    
    
                minn = min(minn, right - left + 1);
                sum -= nums[left];
                left++;
            }
            else if(sum < s) {
    
    
                right++;	
                if(right < nums.size()){
    
    
                	//读取数组元素right的时候,
                	//由于前面有right++,故需要判断是否越界
                    sum += nums[right];
                }
            }
            if(left > right) return 1;
        }
        if(left == 0 && right == nums.size()) return 0;
        else    return minn;
    }
};

替换后的最长重复子字符

在这里插入图片描述

分析

注释写的很好了。。。

class Solution {
    
    
        public int characterReplacement(String s, int k) {
    
    
            int[] num = new int[26];
            int n = s.length();
            int maxn = 0;
            //left:左边界,用于滑动时减去头部或者计算长度
            //right:右边界,用于加上划窗尾巴或者计算长度
            int left = 0, right = 0;
            while (right < n) {
    
    
                int indexR = s.charAt(right) - 'A';
                num[indexR]++;
                //求窗口中曾出现某字母的最大次数
                //计算某字母出现在某窗口中的最大次数,窗口长度只能增大或者不变(注意后面left指针只移动了0-1次)
                //这样做的意义:我们求的是最长,如果找不到更长的维持长度不变返回结果不受影响
                maxn = Math.max(maxn, num[indexR]);
               
                //长度len=right-left+1,以下简称len
                //len-字母出现最大次数>替换数目 => len>字母出现最大次数+替换数目
                //分析一下,替换数目是不变的=k,字母出现最大次数是可能变化的,因此,只有字母出现最大次数增加的情况,len才能拿到最大值
                //又不满足条件的情况下,left和right一起移动,len不变的
                if (right - left + 1 - maxn > k) {
    
    
                    //这里要减的,因为left越过该点,会对最大值有影响
                    num[s.charAt(left) - 'A']--;
                    left++;
                }
                //走完这里的时候,其实right会多走一步
                right++;
            }
            //因为right多走一步,结果为(right-1)-left+1==right-left
            return right - left;
        }
}

K 个不同整数的子数组

在这里插入图片描述

分析

在这里插入图片描述

class Solution {
    
    
public:
    int atMostKDistinct(vector<int>& A, const int K) {
    
    
        int len = A.size();
        int* freq = new int[len + 1];
        memset((void *)freq, 0, sizeof(int)*(len+1));

        int left = 0;
        int right = 0;
        int count = 0;	// [left, right) 里不同整数的个数
        int res = 0;
        // [left, right) 包含不同整数的个数小于等于 K
        while (right < len) {
    
    
            if (freq[A[right]] == 0) {
    
    
                count++;
            }
            freq[A[right]]++;
            right++;

            while (count > K) {
    
    
                freq[A[left]]--;
                if (freq[A[left]] == 0) {
    
    
                    count--;
                }
                left++;
            }
            // [left, right) 区间的长度就是对结果的贡献
            // 以[1 2 1 2 3] k=2 为例,当l=0,r=2时,从右往左数1,21,121既有3个结果
            res += right - left;
        }
        return res;
    }

    int subarraysWithKDistinct(vector<int>& A, int K) {
    
    
        return atMostKDistinct(A, K) - atMostKDistinct(A, K - 1);
    }
        
};

猜你喜欢

转载自blog.csdn.net/weixin_45005811/article/details/113571384
今日推荐