LC之滑动窗口算法

滑动窗口

基本概念

滑动窗口是一种基于双指针的一种思想,两个指针指向的元素之间形成一个窗口。

分类:
窗口有两类,一种是固定大小类的窗口,一类是大小动态变化的窗口。

应用:
利用滑动窗口获取平滑的数据,如一段连续时间的数据平均值,能够有更好的稳定性,如温度监测。

什么情况可以用滑动窗口来解决实际问题呢?

一般给出的数据结构是数组或者字符串
求取某个子串或者子序列最长最短等最值问题或者求某个目标值时
该问题本身可以通过暴力求解

核心思路

窗口的形成

在具体使用之前,我们知道窗口实际是两个指针之间形成的区域,那关键就是这两个指针是如何移动的。

1.初始时,左右指针left,right都指向第0个元素,窗口为[left,right),注意这里是左闭右开,因此初始窗口[0,0)区间没有元素,符合我们的初始定义
在这里插入图片描述

2.开始循环遍历整个数组元素,判断当前right指针是否超过整个数组的长度,是退出循环,否则执行第3步

3.然后right指针开始向右移动一个长度,并更新窗口内的区间数据

在这里插入图片描述

4.当窗口区间的数据满足我们的要求时,右指针right就保持不变,左指针left开始移动,直到移动到一个不再满足要求的区间时,left不再移动位置
在这里插入图片描述

5.执行第2步

这中间,窗口的更新与维护是很重要的一环,新元素加入窗口,旧元素移出窗口,都需要及时地更新与这个窗口范围相关的数据。

上述说明主要是两个while循环,可以简单抽象成一个模板如下:

int left = 0,right =0;
while(right指针未越界){
  char ch = arr[right++];
  //右指针移动,更新窗口
  ...
  
  //窗口数据满足条件 对于固定窗口而言,就是窗口的大小>=固定值;对于动态窗口,就是从left出发,窗口不断扩充,第一次满足题意的位置
  while(窗口数据满足条件){
  	//记录或者更新全局数据
  	...
  	
  	//右指针不动,左指针开始移动一位
  	char tmp = arr[left++];
  	
  	//左指针移动,窗口缩小,更新窗口数据
  	...
  }
  //返回结果
  ...
}

应用实例

LC3.无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

 

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
 

提示:

0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

思路

  • 暴力解法时间复杂度较高,会达到 O(n^2),故而采取滑动窗口的方法降低时间复杂度
  • 定义一个 map 数据结构存储 (k, v),其中 key 值为字符,value 值为字符位置 +1,加 1 表示从字符位置后一个才开始不重复
  • 我们定义不重复子串的开始位置为 left,结束位置为 right
  • 随着 right不断遍历向后,会遇到与 [left, right] 区间内字符相同的情况,此时将字符作为 key 值,获取其 value 值,并更新 right,此时 [left, right] 区间内不存在重复字符
  • 无论是否更新 left,都会更新其 map 数据结构和结果 rs。
    时间复杂度:O(n)
    代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int rs=0;
        Map<Character,Integer> map = new HashMap<>();
        for(int left=0,right=0;right<s.length();right++){
            char c=s.charAt(right);
            if(map.containsKey(c)){
                left=Math.max(map.get(c),left);
            }
            rs=Math.max(rs,right-left+1);
            map.put(s.charAt(right),right+1);
        }
        return rs;
    }
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值