【专刷】滑动窗口(一)
📝前言说明:
- 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,按专题划分
- 每题主要记录:(1)本人解法 + 本人屎山代码;(2)优质解法 + 优质代码;(3)精益求精,更好的解法和独特的思想(如果有的话)
- 文章中的理解仅为个人理解。如有错误,感谢纠错
🎬个人简介:努力学习ing
📋本专栏:C++刷题专栏
📋其他专栏:C语言入门基础,python入门基础,C++学习笔记,Linux
🎀CSDN主页 愚润泽
视频
- 滑动窗口的关键
- 209. 长度最小的子数组
- 个人解
- 深刻理解滑动窗口
- 3. 无重复字符的最长子串
- 个人解
- 1004. 最大连续1的个数 III
- 个人解
- 1658. 将 x 减到 0 的最小操作数
- 个人解
- 优质解:
滑动窗口的关键
滑动窗口:同向双指针(即left
和right
移动方向相同,且right
不回退)
滑动窗口是:通过动态调整窗口的左右边界来避免重复计算,从而降低时间复杂度。
其核心思想是维护一个窗口,通过移动右边界扩展窗口、移动左边界收缩窗口,找到满足条件的解。(窗口的状态始终要满足条件)
滑动窗口前提:
- 同向双指针
- 操作的窗口是连续子数组
- 有“单调性”,这种单调是指:可以根据窗口来推出其他的子数组是否满足条件,如:在一个非负数组里面找子数组,如果窗口内的值之和>
target
,则right
往右移动,更大的窗口内的值之和肯定也大于target
代码要点:
left
,right
标记窗口的左区间和右区间- 进窗口(更新要维护的值)
- 判断(新元素进窗口后,维护的值怎么变化),由判断结果决定:出窗口(弄清楚判断满足什么条件的时候,需要出窗口)
- 更新结果(就题论题,有些是出窗口更新结果,有些是判断的时候更新)
进出窗口是:左右指针的移动
209. 长度最小的子数组
个人解
思路:
本题维护的是子数组内元素的和sum
,当sum >= target
的时候就可以更新结果,然后出窗口,当不满足条件的时候需要进窗口。
屎山代码:
class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int n = nums.size(), ans = n + 1, sum = 0; // sum 是要维护的值for(int left = 0, right = 0; right < n; right++){sum += nums[right]; // 进窗口while(sum >= target) // 判断{ans = min(right - left + 1, ans); // 更新结果sum -= nums[left++]; // 出窗口}}return ans <= n ? ans : 0;}
};
时间复杂度:O(n)
空间复杂度:O(1)
深刻理解滑动窗口
我们通过这道题再剖析一下滑动窗口,这道题为什么能使用滑动窗口,从暴力解法到滑动窗口的优化。
暴力解法:
left
从左向右遍历数组,right
在每个left
下,再遍历数组,获得每个子数组的信息。时间复杂度O( n 2 n^2 n2)
如何优化成滑动窗口?
滑动窗口的正确性就是:单调性保证的,利用单调性规避了很多没有必要的枚举!
3. 无重复字符的最长子串
个人解
思路:
维护(判断):窗口内的每个字符出现个数不超过 1
因为本题是找最长:所以,满足条件的时候尽可能的进窗口。
而上一题,是找最短,所以当满足条件的时候是出窗口。
用时:5:00
屎山代码:
class Solution {
public:int lengthOfLongestSubstring(string s) {int n = s.size(), ans = 0;map<char, int> cnt;for(int left = 0, right = 0; right < n; right++){cnt[s[right]]++; // 进窗口while(cnt[s[right]] > 1) // 判断{cnt[s[left]]--;left++; // 出窗口}ans = max(ans, right - left + 1); // 更新结果}return ans;}
};
时间复杂度:O(n)
空间复杂度:O(1)
1004. 最大连续1的个数 III
个人解
思路:
- 维护变量:窗口内的
0的个数要<=k
- 进窗口:进窗口时更新窗口内变量的值(通过判断进入的是不是0)
- 判断(出窗口操作一般依托于判断):什么时候需要出窗口?当窗口内的数量大于k,所以这就是判断条件
- 出窗口:随着判断也就顺便完成了。出窗口时也需要更新窗口内维护的变量的值。
- 更新变量:因为判断的是不满足的条件,所以在出窗口后更新(出窗口后一定满足条件)
用时:6:48
屎山代码:
class Solution {
public:int longestOnes(vector<int>& nums, int k) {int ans = 0, n = nums.size(), len = 0;for(int left = 0, right = 0; right < n; right++){if(nums[right] == 0){k--;}len += 1; // 进窗口while(k < 0) // 判断{if(nums[left] == 0){k++;}left++; // 出窗口len--;}ans = max(len, ans);}return ans;}
};
时间复杂度:O(n)
空间复杂度:O(1)
1658. 将 x 减到 0 的最小操作数
个人解
思路:
将题目转换成:求满足元素和==target的最长子数组
先对数组元素求和sum。然后target == sum - x
用时:13:00
屎山代码:
class Solution {
public:int minOperations(vector<int>& nums, int x) {int target = std::accumulate(nums.begin(), nums.end(),0) - x;if(target < 0){return -1;}if(target == 0){return nums.size();}int n = nums.size(), sum = 0, len = 0;for(int left = 0, right = 0; right < n; right++){sum += nums[right]; // 入窗口while(sum >= target) // 判断{if(sum == target){len = max(right - left + 1, len); // 更新结果}sum -= nums[left]; // 出窗口left++;}}return len == 0 ? -1 : n - len; }
};
时间复杂度:O(n)
空间复杂度:O(1)
优质解:
看了一下别人的题解,用滑动窗口的也是这种逆向思维转换一下。
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!