双指针算法——滑动窗口

前言:

滑动窗口本质上也是利用双指针来解决特定情况下的问题。滑动窗口算法思想是通过俩个指针,定义在左边和右边,俩指针同向运动,保持着一个像"窗口"一样的双指针来不停的压缩或者扩展来移动"窗口",从而找到特定的子数组。

滑动窗口基本做题思路:

首先我们可以利用暴力解法来看看优化,是否是利用双指针来解决?双指针是否同向移动?

如果满足,可以定义为使用滑动窗口来解决,时间复杂度可以大大优化(一般为0(N))

进窗口(right指针进入,相当于扩展窗口大小,框住子数组)

判断(判断题目给定条件是否成立,要是不符合该指定条件,则left指针选择移动)

出窗口(left指针移动,相当于缩小窗口大小,直到满足题目要求即可)

更新结果(更新题目所求,得出最优结果)

一、长度最小的子数组(. - 力扣(LeetCode)

如图所述,题目要求找出一连串的子数组,其数据之和要大于等于所给定的target,而且该子数组长度是最小的,以示例一举例:

算法思路:

我们可以利用双指针来不断寻找,定义在一个"窗口"里面来找他们元素之和是否大于等于给定条件的数,先"进窗口",right指针不断插入,遍历一遍数组,然后再判定子数组里面的元素之和是否大于等于target,若是符合,则进入"出窗口"阶段,更新最小长度,将left、继续右移,判断是否符合;若是不符合,则继续由right扩展窗口:

代码实现:

二、最大连续1的个数 (. - 力扣(LeetCode)

如图所述,给定一个数组和目标数k,题目要求是最多翻转k个0,也就是说只要不超过k次,翻转多少都可以。

相当于就是在数组中把为0的数翻转成1,最多翻转k次,然后再择出最长的连续的1的子数组,并且返回该长度。

算法思路:

首先通过暴力解法可知道双指针是同向移动,从中得知可以使用滑动窗口算法,先定义一个左右指针,然后为了不改变原数组的任何数据,我们可以使用一个计数器来统计"窗口"里面的0个数:

代码实现:

三、将x减到0的最小操作数(. - 力扣(LeetCode)

如图所述,题目要求是每一次除去左端或右端数,除去的数相加若刚好为0,则返回除去的元素数量,若无法减为0,这返回-1。

算法思路:

对于该题,常规思路不好解,代码也会很复杂。我们可以逆向思维来解题:题目告知我们需要除去左端或右端元素来解决问题,但是我们可以从中间那一段来使用滑动窗口算法解决问题:

逆向思维想好思路后利用暴力解法,我们可以再次转化为我们熟悉的滑动窗口,只需从中间选出nums_sum - (a+b) 的元素之和即可,利用"窗口"来框住子数组,剩下的就是题目所需要的最终答案了,找出目标数可以参考第一题,基本逻辑是一样的:

代码实现:

四、找到字符串中所有字母异位词(. - 力扣(LeetCode)

如图所述,题目要求是返回p中的所有异位词,异位词的定义为:

这些都称为异位词,只需要将字符串中连续的子串异位词的开始索引记录返回即可

算法思路:

依旧是使用滑动窗口来解决,但是该题毕竟不是数组,也不能使用相加的方式来做。我们可以使用哈希表来完成该题。将p中的各个字符放入哈希表中记录下来,再定义一个用于统计s中符合的子串的哈希表,最终判断俩哈希表是否相同来返回起始索引:

代码实现:

注:由于题目要求只有26个字母,所以我们可以使用数组来代替哈希表,节省时间空间复杂度

public List<Integer> findAnagrams(String s, String p) {

List<Integer> list = new ArrayList<>();

int[] hash1 = new int[26];//让普通数组代替hash表,26个字母

int[] hash2 = new int[26];

int p_len = p.length();

int len = 0;

for (int i = 0; i < p_len; i++) hash1[p.charAt(i) - 97]++;//创建hash1表

for (int left=0,right=0; right < s.length();right++){

hash2[s.charAt(right) - 97] += 1;

len++;//进窗口

if(len > p_len){//判断

hash2[s.charAt(left)-97]--;

left++;//出窗口

len--;

}

if(this.My_equals(hash1,hash2)) list.add(left);//更新结果

}

return list;

}

public boolean My_equals(int[] hash1,int[] hash2) {

for (int i = 0; i < hash2.length; i++)

if (hash1[i] != hash2[i])

return false;

return true;

}

五、最小覆盖子串(. - 力扣(LeetCode)

如图所述,只需要包含t中所有的字符内容都可以,不论包含几个,只需要大于等于t中的字符即可,最终返回最小的子串。

算法思路:

该题与上题类似,但也有不同。区别是上题只需要统计个数即可,但此题是只论种类,不论数目.

我们依旧是用到哈希表这个容器来帮助我们完成该题。为了防止像上题一样循环26次才得出俩哈希表是否相同,我们可以使用一个小小的优化来进行改造:

然后就可以使用我们的滑动窗口来解决该问题了:

代码实现:

public String minWindow(String s, String t) {

int[] hash1 = new int[128];//hash来代替哈希表(26+26个字母)

int[] hash2 = new int[128];

char in = 0, out = 0;//用来统计出入窗口时的字符

int hash1_size = 0, len = Integer.MAX_VALUE, flg = 0;//用于统计t的种类,和最短长度,起始位置

for (char x : t.toCharArray()) {

hash1[x] += 1;

if (hash1[x] == 1) hash1_size++;//只有新种类进来时才加1

}

for (int left = 0, right = 0, count = 0/*维护hash2的种类*/; right < s.length(); right++) {

in = s.charAt(right);

hash2[in]++;//进窗口

if (hash1[in] == hash2[in]) count++;//只有相等时才加1,防止重复种类的相加

while (count == hash1_size) {//判断

out = s.charAt(left);

if (len > right - left + 1) {

len = right - left + 1;//更新长度

flg = left;//更新起始位置

}

if (hash1[out] == hash2[out]) count--;

hash2[out]--;

left++;//出窗口

}

}

if(len == Integer.MAX_VALUE) return "";

else return s.substring(flg,flg + len);

}

以上就是关于滑动窗口的经典例题,希望对大家有所帮助,谢谢各位观看!

相关推荐
大数据编程之光19 分钟前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
爪哇学长32 分钟前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
Dola_Pan36 分钟前
C语言:数组转换指针的时机
c语言·开发语言·算法
ExiFengs36 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj12345678938 分钟前
JDK1.8新增特性
java·开发语言
繁依Fanyi1 小时前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
慧都小妮子1 小时前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m51271 小时前
LinuxC语言
java·服务器·前端
烦躁的大鼻嘎1 小时前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
IU宝1 小时前
C/C++内存管理
java·c语言·c++