目录
一、了解滑动窗口
1. 什么是滑动窗口?
滑动窗口这个方法听起来挺新鲜的,其实本质上就是双指针法,调整头尾指针使夹在指针之间的子区间像窗口一样在整个数组中移动
2. 滑动窗口能解决哪类题型?
滑动窗口一般用在求符合条件的最短(最长)子数组的长度
(我目前为止也就遇见我这类题目,若遇见其他类型我会立刻更新,与大家分享)
二、答题模板
以上是我自己总结出来的答题模板,(以 i 为头指针,以 j 为尾指针)用滑动窗口解决问题代码大致如此,具体问题还得具体分析,没有看懂这个模板可以直接进入下面的两道题目找找感觉。
🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗 🚗
三、经典例题
209. 长度最小的子数组
leetcode传送➡️https://leetcode.cn/problems/minimum-size-subarray-sum/
给定一个含有
n
个正整数的数组和一个正整数target
。找出该数组中满足其和
≥ target
的长度最小的 连续子数组[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回0
。示例 1:
输入:target = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3]是该条件下的长度最小的子数组。
题目分析:
本题要求我们找到子数组总和大于等于target的最短长度,是一个典型的滑动窗口题;
定义一个sum记录总和;
右指针 j 用for循环移动,每指向一个值就加入到sum表示从i到j这一段子数组中的元素总和;
当sum >= target 时,及满足题目条件,记录此时长度并比较,并更新窗口(i ++)直到窗口内的总和小于target
#define INT_MAX 2147483647
int minSubArrayLen(int target, int* nums, int numsSize) {
int result = INT_MAX; //求最小值,需要初始化为最大值
int i = 0;
int sum = 0;
for (int j = 0; j < numsSize; j++)
{
sum += nums[j];
while (sum >= target)
{
int tmp_len = j - i + 1; //此时窗口长度
result = fmin(result, tmp_len); //取较小值
//更新窗口,左边界往右移
sum -= nums[i];
i++;
}
}
if (sum < target && i == 0) //数组总和都不能达到target的情况
return 0;
return result;
}
四、举一反三
剑指 Offer 48. 最长不含重复字符的子字符串
leetcode传送➡️https://leetcode.cn/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/?favorite=xb9nqhhg
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
题目分析:
此题根上一题本质上没有区别,所以需要我们找出不同之处:
要实现不重复,那么应该怎么记录已经在窗口内的字母呢?不难想到用哈希表记录迅速掌握哈希表的使用https://blog.csdn.net/Dusong_/article/details/127257647?spm=1001.2014.3001.5502当同一字母在窗口内出现两个时,左指针指向的字母的哈希值-1,并缩小窗口(i++),直到没有重复的字母;
最后将没有重复字母的窗口长度记录并比较;
int lengthOfLongestSubstring(char* s) {
int result = 0; //结果长度
int i = 0; //头指针
int hash[200];
memset(hash, 0, sizeof(hash));
for (int j = 0; j < strlen(s); j++) //j为尾指针
{
hash[s[j]]++;
while (hash[s[j]] != 1) //若果有重复元素及hash[s[j]] == 2
{
hash[s[i]]--;
i++; //从头缩小窗口
}
int tmp_len = j - i + 1;
result = fmax(result, tmp_len); //没有重复则收集结果
}
return result;
}
这道题在我第一次做时并没有用滑动窗口,做起来还是比较复杂的,所以掌握正确的方法是非常重要的,下面我还是把我没有用滑动窗口的代码放在这里,大家可以直接忽略,也可以适当看看⬇️
int lengthOfLongestSubstring(char* s) { int max = 1; int head = 0; int hash[150]; memset(hash, -1, sizeof(hash)); //哈希表的值表示字母坐标 hash[s[0]] = 0; for (int i = 1; i < strlen(s); i++) { if (hash[s[i]] != -1) //序列中有重复 { for (int j = head; j < hash[s[i]]; j++) { hash[s[j]] = -1; //更新哈希表:将前一个重复元素之前的置为-1 } head = hash[s[i]] + 1; //将头设置成前一个重复元素后面的一个位置 hash[s[i]] = i; //该字母再哈希表的位置的值重新设置成新的坐标 } else { hash[s[i]] = i; //没有重复,记录该字母的坐标位置 } max = fmax(max, (i - head + 1)); } if (size == 0) return 0; return max; }
Summery💐💐💐
理解透彻滑动窗口题,其实发现它很简单,总结下来需要记住一下几点:
① 用for循环移动窗口右边界
② 用while循环更新窗口
③ 从左边界往右移为缩小窗口,右边界往右移扩大窗口
④ 达到条件就记录并比较结果