文章目录
主要解决的问题
数组里的每个子数组的性质
- 通常说给定一数组nums,让我们求一段子数组所满足的性质,如nums中最短之和大于target的子数组长度。
数组里的每个子数组里面元素的性质
- 求给定一子数组长度,让我们求子数组中元素的最值,还有就是求其各子数组之和或者积。
这类问题往往可以借助前缀和,只要确认前缀数组的长度就行,下面几个题目都十分经典,没有明白尽可能要弄明白或者看看讲解视频。
1.滑动窗口模板和步骤(第一类问题)
1.1 模板+长度最小数组例题
上来如果直接说模板,感觉有点太唐突了,先结合一个简单例题体会一下。
问题是求最短的子数组长度并满足要大于等于target,也就是我们的第一类问题。
思路: 使用两个下标一前一后,不断累加,刚好大于等于target时候,就记录下来 R - L + 1的长度,来每次比较这个长度求最小。下面看代码。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int sum = 0,minlen = 2e9;
// l 和 r 类似双指针一样,一前一后
for(int l = 0,r = 0;r<nums.size();r++ ){
//进窗口,累加进窗口
sum += nums[r];
//判断条件
while(sum>=target){
//记录更新答案
minlen = min(minlen,r-l+1);
//出窗口
sum -= nums[l++];
}
}
//返回结果
return minlen == 2e9?0:minlen;
}
};
步骤:
1.使用两个下标来维护一段数组区间
2.开始进窗口,得到问题所满足的
3.进窗口时候,判断是否条件已经满足,满足了更新结果
4.更新结果之后,要及时出窗口
5.一些细节,比如刚开始就条件不满足,要判空等等。
1.2 条件+最大连续1的个数
这问题和上面问题其实道理一样,加了一些判断条件,难题就是简单题加条件出来的。我们只要好好记录最初的模板,然后中等和难题分析其中的条件就行。
思路: 和第一题类似,我们只要判断一下是0的情况,加一个计数就行,统计0的数目。
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int l = 0,r = 0,maxlen = 0,n = nums.size();
//模拟一个队列 h队头,t队尾
int h = 0,t = 0;
int mk[100000]={0};
while (r < n) {
//等于0的情况
if (nums[r] == 0){
//填加到队列里面
mk[t++] = r;
//队列里面的0数量大于了k
if (t-h > k) {
// +1是跳到第一个0的后一个位置,
l = mk[h] + 1;
//把一个0出队
h++;
}
}
maxlen = max(maxlen,r-l+1);
++r;
}
return maxlen;
}
};
如果你做完两道题目,肯定对滑动窗口有一些感觉,模板就是这样吗。变得只是不同题目的限制条件。熟悉了基本模板套路就行,不要说我一定要会所有的滑动窗口题目,或者一类,只要会6,7成这类算法就可以过了。
1.3 思考+将x减到0的最小操作数
我这里默认你已经通过了前面两个题目?下面我们看看下面这个
思路: 也就是说题目让我们求左右两边数组元素之和等于X,还是最短的。我们反过来求中间数组元素之和等于 SUM - X,且最长的就行了。其他步骤和第一题一模一样,这题通过率只要39%,所以模板题一定要掌握。
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
//nums所有元素和
int sum = 0;
for(auto k:nums) sum+=k;
if(sum<x) return -1; //sum<x,直接返回
//if(sum==x) return nums.size();
int maxlen = -1;
//在数组中间寻找值为sum-x的最长子数组
for(int l = 0,r = 0,tmp = 0;r<nums.size();r++){
tmp += nums[r];
while(tmp > sum - x ){
tmp -= nums[l++];
}
if(tmp == sum -x) maxlen = max(maxlen,r-l+1);
}
return maxlen==-1?-1:nums.size()-maxlen;
}
};
2.单调队列模板和步骤(第二类问题)
也关于从子数组的元素性质的一些问题。
2.1 单调队列概念
初步简单认识
1.单调队列是一个数据结构,并不是STL里面的内容。
2.单调队列为何说单调,因为是队列中的元素始终保持着单增或者单减的特性。(注意始终保持这四个字)
3.单调队列不只是做到了排序,还可以实现一个功能:在每次加入或者删除元素时都保持序列里的元素有序,即队首元素始终是最小值或者最大值,这个功能非常重要,单调队列我们就是使用的这个功能。
2.2 滑动窗口最大值
也没有很难,有的难题没有中等题目难得,大家理清思路就行。
思路: 具体步骤就是保存每次加入元素,保持队列元素递增或者递减,如果不满足就不加入。窗口满了就出元素。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
deque<int> q;
//还没有形成窗口
//使用第一个窗口去初始化单调队列
for (int i = 0; i < k; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);//队列存储的是下标
}
//ans存储每个窗口的最大值
vector<int> ans = {nums[q.front()]};
//形成了窗口
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
//检查最大元素是否在窗口区间里
if (q.front() <= i - k) {
q.pop_front();
}
ans.push_back(nums[q.front()]);
}
return ans;
}
};
3.滑动窗口力扣题单
下面总结了一些关于滑动窗口得经典题目,感兴趣得可以做做。