数组常用算法之滑动窗口

·  阅读 144
数组常用算法之滑动窗口

题目

先用一道leetcode算法题目引出本文算法,题目链接是209image.png

暴力解法

这道题目暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列,毫无疑问,时间复杂度很明显是O(n^2) 。

代码

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int result = Integer.MAX_VALUE; // 最终的结果
        int sum = 0; // 子序列的数值之和
        int subLength = 0; // 子序列的长度
        for (int i = 0; i < nums.length; i++) { // 设置子序列起点为i
            sum = 0;
            for (int j = i; j <  nums.length; j++) { // 设置子序列终止位置为j
                sum += nums[j];
                if (sum >= target) { // 一旦发现子序列和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;
                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}
复制代码

复杂度

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

滑动窗口算法

滑动窗口算法,其实就是不断地调节子序列的起始位置终止位置,从而得出要想的结果,以本题目为例:

640.gif

在前后两个指针的移动过程中,会找到如下几个满足条件的窗口: 【2,3,1,2】, 【3,1,2,4】,【1,2,4】,【2,4,3】,【4,3】 最终【4,3】为满足解题条件的最优解

滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。

在本题中实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。窗口的起始位置如何移动: 如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。解题的关键在于 窗口的起始位置如何移动,如图所示:

image.png 滑动窗口的难点和精髓就在于在找到满足条件的窗口之后,前后两个指针怎么移动:

  while (sum >= target) { 
        //计算满足条件的窗口长度
        subLength = Math.min(subLength, right - left + 1); 
        //这里其实也可以分开写,sum先减去左边指针的值,然后左边指针往右移动
        sum -= array[left++]; 
        }
    } 
复制代码

完整代码

class Solution { 
    public int minSubArrayLen(int target, int[] array) { 
        int length = array.length; int subLength = length + 1; 
        int sum = 0; int left = 0; 
        for (int right = 0; right < length; right++) { 
            sum += array[right]; 
            while (sum >= target) { 
                subLength = Math.min(subLength, right - left + 1); 
                sum -= array[left++]; 
                }
            } 
        return subLength > length ? 0 : subLength; 
        } 
}
复制代码

复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

【参考】
【1】github.com/youngyangya…

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

分类:
后端
标签: