209.长度最小的子数组
题目解析:
我们先从暴力破解的角度进行优化来过渡到滑动窗口的理论。
正常按照暴力就是先设一个sum来记录遍历指针right经过的数值,若sum<target就让right继续往下遍历,直到sum>=target的时候记录长度,持续到一轮结束后就移动left缩减范围进行新一轮的比较。最后就是挑出最小的len作为返回结果。
算法解析:
而滑动窗口算法就是在单调性这里对暴力算法作出优化。
在第一轮中当我们已经达到sum>=target的条件后就可以结束这一轮了,再继续往后遍历只会让长度更长(因为数组全为正整数).
而在第一轮结束后right也不用急着复位(与left共同指向),保持这个窗口。而窗口内的sum数值更新只需要sum-=2就行了。
滑动窗口流程图:只要没达到预期值,那我们就不断扩充窗口直到sum达成目标值。这时候先计算长度,然后开始缩小窗口(开始第二轮)。而无论是扩充窗口还是缩小窗口后面都需要判断,扩充完窗口后得看是继续扩充呢?还是先计数再缩小~缩小完窗口后得看是继续计数缩小呢?还是选择扩充。(因为无论扩窗还是缩窗sum都会发生改变,所以得进一步判断)
代码:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size(), sum = 0, ret = INT_MAX;
for (int left = 0, right = 0; right < n; right++)
{
//扩窗口
sum += nums[right];
//判断是否继续扩充,若发现需要扩充,进入下一处循环
//若发现已经扩充到目标值时,准备缩小
while (sum >= target)
{
ret = min(ret, right - left + 1);//计数
sum -= nums[left++];//缩窗口,然后再转回循环判断是否需要继续缩窗
}
//发现不需要继续缩窗了,进入循环进行再次扩充
}
return ret == INT_MAX ? 0 : ret;
}
};
3.无重复字符的字符子串
题目解析:
这道题和上一道思路基本一致,先用暴力进行优化过渡到滑动窗口~
利用right指针遍历数组,每次遍历前都得利用哈希表判断一下,表内没有就添加进去,表内有那就停止遍历,先把重复项移出直到没有重复后最终才计算长度。
算法解析:
而滑动窗口算法就是对暴力解法的进一步优化~
在找到重复项后right不用复位,因为在第一轮中已经把遍历过的数都放在哈希表内了,而left与right又都为重复项,所以在新的一轮中只需要通过left来去除哈希表的其中一个重复项然后再移动就好了。
滑动窗口流程:
无重复项——>扩充窗口(若仍无则继续扩充)(若有重复项,准备缩小窗口)
有重复项——>缩小窗口(仍有重复项,例如pwwkew继续缩小))(若无,准备扩充)
计算长度要再最后执行,不能放在缩小窗口循环内。这也是跟上一题的区别(上一题是通过缩小窗口来求长度最小,所以每一次缩小都有可能得到最小长度)(而本题是求最大连续长度,只需要在去除所有重复项后计算就行了)
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n = s.size();
int ret = INT_MIN;
int hash[128] = { 0 };//用数组模拟哈希表
for (int left = 0, right = 0; right < n; right++)
{
//扩充窗口
hash[s[right]]++;
//判断,当扩充完后若无重复项,计算长度并进入下一循环扩充
//当有重复项时,开始缩小窗口直到没有重复项
while (hash[s[right]] > 1)
{
//缩小窗口
hash[s[left++]]--;
}
ret = max(ret, right - left + 1);//没有重复项时计长度
//计完长度后继续进入下一循环扩充
}
return ret == INT_MIN ? 0 : ret;
}
};