算法基本思想
首先对于滑动窗口,题目可以先去看看leetcode 209 进行相关的了解后,再来书写代码。
首先我们的第一想法肯定就是暴力解法:也就是采用两层循环进行遍历,寻找相关的运算并得到最后的答案。下面是暴力的解法:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX; // int的最大值
int sum = 0; // 子序列的数值之和
int subLength = 0; // 子序列的长度
for (int i = 0; i < nums.size(); i++) {
// 设置子序列起点为i
sum = 0;
for (int j = i; j < nums.size(); j++) {
// 设置子序列终止位置为j
sum += nums[j];
if (sum >= s) {
// 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
而对于这个代码进行分析的话,我们会发现他的时间复杂度是O(n2),实际上对于这个量级的数据,我们这种算法实际上已经需要更新的了,很容易超时。
那有没有一种方法可以有效的减少时间复杂度呢?回忆一下之前part2部分的内容,我们实际上是需要两层for循环来遍历数组的事情,我们采用双指针法来实现得到了一层循环做到了两层循环做到的事情,故双指针法是一条道路。
双指针法的实现过程:
int function1(int target, vector<int>& nums) {
//首先我们先可以看到这里,我们决定采用双指针法,来仅仅采用一次循环达到题目所需要的要求。
//先定义具体的指针。sum的值实际上就是数组从begin开始加到end为止
int begin = 0, end = 0;
int sum = 0;
//对于我们来说,我们第一步的想法肯定是,遍历begin然后调整end来进行控制中间数据的叠加,但是当你写下begin++开始变化的时候
//你会发现,后面的end随之变化,我们最后写出来的代码实际上也就是跟两层for循环一样,实际上也就是和暴力算法没有区别。
for ( begin = 0; begin < nums.size(); begin++)
{
//定义sum 并且变化end
//for 循环从begin相加到end
}
}
上面的function的方式,我们似乎没有逃开两层循环的限制。所以我们来分析一下,为什么开始begin不能等于0,实际上我们想要让整个函数的运行仅仅只有一层for循环,我们所需要做的事情就是,随着循环的变化,begin和end仅仅只有一个自由度的变化。所以我们需要采用end的变化,来进行操作,看不懂的可以看看代码,然后理解一下过程。
int function1(int target, vector<int>& nums) {
//首先我们先可以看到这里,我们决定采用双指针法,来仅仅采用一次循环达到题目所需要的要求。
//先定义具体的指针。sum的值实际上就是数组从begin开始加到end为止
//current_len统计的是从begin到end之间的数据有多少个
//result返回最后的最短窗口 初始最大的int数,方便操作
int begin = 0, end = 0;
int sum = 0;
int current_len = 0, result = INT32_MAX;
//采用end++的变化
for ( end = 0; end < nums.size(); end++)
{
sum += nums[end];
//这里if注意一下,我们先按照自己感性的想法来写
if (sum >= target)
{
//当中间的数字大了,我们实际上就可以统计窗口的大小,并且将begin进一位,然后再次统计了。
current_len = end - begin + 1;
//选择小的窗口大小
result = result < current_len ? result : current_len;
//准备下一次的循环,我们发现现在的数据已经大于target了,所以我们需要减掉begin的值,将左指针向右移动,然后后续右指针再进行移动窗口,得到最后的值
sum -= nums[begin