算法笔记【四】滑动窗口

算法思想

01 题目关键字

  1. 主要用来处理连续的问题,输入的是一个数组或者是字符串,求解具有某个性质的子数组或者是子字符串
  2. 所谓滑动窗口,就是不断调节子序列的起始位置和终止位置,从而得到我们所需要的答案
  3. 本质上就是双指针算法,用两个指针来维护这个窗口
  4. 主要有两种类型
    1. 固定窗口
    2. 可变窗口:寻找最长 和 寻找最短

02 核心思想

2.1 寻找最长

  1. 核心:左右双指针位于起始点,右指针向右逐位滑动循环
  2. 每次滑动过程中
    1. 如果说,窗内元素满足条件,右指针向右扩大窗口,并更新最优结果
    2. 如果说,窗内元素不满足条件,左指针向右缩小窗口
  3. 右指针到达结尾
int left, right, result, bestResult;

while(右指针没有到达结尾){
    
	窗口扩大,加入right对应的元素,更新当前的result;
    
    while(result 不满足 条件) {
    
        窗口缩小,移除left对应的元素,left 右移
    }
    
    更新最优解
        
    右指针向右移动
 
}

2.2 寻找最短

  1. 核心:左右双指针位于起始点,右指针向右逐位滑动循环
  2. 每次滑动过程中
    1. 如果说,窗内元素满足条件,左指针向右缩小窗口,并更新最优结果
    2. 如果说,窗内元素不满足条件,右指针向右扩大窗口
  3. 右指针到达结尾
int left, right, result, bestResult;

while(右指针没有到达结尾){
    
	窗口扩大,加入right对应的元素,更新当前的result;
    
    while(result 满足 条件) {
    
        窗口缩小,移除left对应的元素,left 右移
    }
    
    更新最优解
        
    右指针向右移动
 
}

209.长度最小的子数组

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n  = nums.length;
        if(n == 0) return 0;
        int result = Integer.MAX_VALUE;
        int left = 0;
        int right = 0;
        int sum = 0;
        while(right < n){
            sum += nums[right];
            while(sum >= target) {   
                int len = right - left + 1;           
                result = result < len ? result : len;               
                sum -= nums[left++];       
            }
            right ++;
        }
        if(result == Integer.MAX_VALUE){
            return 0;
        }
        return result;
    }
}

1456. 定长子串中元音的最大数量

通过题目进行分析,这个是一个滑动窗口类型的题目。也可以理解为双指针。

  1. 题目中定义的就是一个长度是 k 的子序列
  2. 我们用两个指针来维护这个窗口,题目中要求长度是k,所以说我们要求两个指针同时移动来维护窗口固定这个性质。
  3. 每次移动窗口中都会进行一个新的元素,并且移除一个元素。如果说原来窗口中元音的个数是res,
    1. 如果说划出去的字符是元音,那么说新窗口中元音的个数就是 res - 1;反之不变
    2. 如果说新加入的字符是元音,那么说新窗口中元音的个数就是 res + 1;反之不变
  4. 通过上面的分析,我们可以用 1 来标志元音,用 0 来标志非元音。原来的字符串就等价于01字符串
    1. 新窗口中元音的个数 = 原窗口中元音的个数 + 右指针对应的数字 - 左指针对应的数字
class Solution {
    public int maxVowels(String s, int k) {
        int n = s.length();
        if(n == 0 || n < k) return 0;
        int[] help = new int[26];
        help['a' - 'a'] = 1;
        help['e' - 'a'] = 1;
        help['i' - 'a'] = 1;
        help['o' - 'a'] = 1;
        help['u' - 'a'] = 1;
        int res = 0;
        for(int i = 0; i < k; i++){
            if(help[s.charAt(i)-'a'] == 1) res++;
        }
        int left = 0;
        int tem = res;
        for(int right = k; right < n; right++){
            tem = tem + help[s.charAt(right) - 'a'] - help[s.charAt(left) - 'a'];
            res = Math.max(res,tem);
            left++;
        }
        return res;
    }
}

剑指Offer. 滑动窗口的最大值

单调队列

本题是单调队列的典型题目。单调队列并不是严格意义上的队列,因为为了维护队列的单调性,队尾允许出队元素。
如果说队列中有两个元素arr[i] >= arr[j] 且 i < j,无论到什么时候,我们都不会选择arr[i]作为窗口的最小值,所以说直接将arr[i] 删除。此时队列中剩下的元素,一定是单调递增的。也就说队头的元素是整个队列中的最小值。
为了维护队列的这个性质,在往队列中添加元素的时候,先将队尾大于等于当前数的全部元素进行删除。如果说此时队列的大小超过窗口值,则队头删除元素。

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        if(n == 0 || n < k) return new int[]{};
        List<Integer> list = new ArrayList<>();
        int[] q = new int[n+1];
        int hh = 0, tt = -1 ,index =0;
        for(int i = 0; i < n; i++){
            if(hh <= tt && i - k + 1 > q[hh]) {
                hh++;
            }
            while(hh <= tt && nums[q[tt]] <= nums[i]){
                tt--;
            }
            q[++tt] = i;
            if(i >= k-1) {
                index++;
                list.add(nums[q[hh]]);
            }
        }
        int[] res = new int[index];
        for(int i = 0;i < index; i++){
            res[i] = list.get(i);
        }
        return res;

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

佩奇inging

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值