《算法通关村——原来滑动窗口如此简单》

《算法通关村——原来滑动窗口如此简单》

基本思想

滑动窗口的思想非常简单,如下图所示,假如窗口的大小是3,当不断有新数据来时,我们会维护一个大小为3的一个区间,超过3的就将新的放入老的移走。

这个过程有点像火车在铁轨上跑,原始数据可能保存在一个很大的空间里(铁轨),但是我们标记的小区间就像一列长度固定的火车,一直向前走。

有了区间,那我们就可以造题了,例如让你找序列上三个连续数字的最大和是多少,或者子数组平均数是多少(LeetCode643)等等。

《算法通关村——原来滑动窗口如此简单》_第1张图片

从上面的图可以看到,所谓窗口就是建立两个索引,left和right,并且保持{left,right}之间一共有3个元素,然后一边遍历序列,一边寻找,每改变一次就标记一下当前区间的最大值就行了。

这个例子已经告诉我们了什么是窗口、什么是窗口的滑动:

  • 窗口:窗口其实就是两个变量left和right之间的元素,也可以理解为一个区间。窗口大小可能固定,也可能变化,如果是固定大小的,那么自然要先确定窗口是否越界,再执行逻辑处理。如果不是固定的,就要先判断是否满足要求,再执行逻辑处理。

  • 滑动:说明这个窗口是移动的,事实上移动的仍然是left和right两个变量,而不是序列中的元素。当变量移动的时,其中间的元素必然会发生变化,因此就有了这种不断滑动的效果。

在实际问题中,窗口大小不一定是固定的,我们可以思考两种场景:

  1. 固定窗口的滑动就是火车行驶这种大小不变的移动 。
  2. 可变的窗口就像两个老师带着一队学生外出,一个负责开路,一个负责断后,中间则是小朋友。两位老师之间的距离可能有时大有时小,但是整体窗口是不断滑动的。

根据窗口大小是否固定,可以造出两种类型的题:

  1. 如果是固定的,则一般会让你求哪个窗口的元素最大、最小、平均值、和最大、和最小等等类型的问题。

  2. 如果窗口是变的,则一般会让你求一个序列里最大、最小窗口是什么等等。

练题

643. 子数组最大平均数 I

给你一个由 n 个元素组成的整数数组 nums 和一个整数 k

请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。

任何误差小于 10-5 的答案都将被视为正确答案。

示例 1:

输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75

示例 2:

输入:nums = [5], k = 1
输出:5.00000

提示:

  • n == nums.length
  • 1 <= k <= n <= 105
  • -104 <= nums[i] <= 104
题解

这里直接就用一次遍历就解决问题。大大提高了解决速度,这就是滑动窗口

class Solution {
    public double findMaxAverage(int[] nums, int k) {
        int len = nums.length;
        int windowSum = 0;
        if(k > nums.length || nums.length < 1 || k<1){
            return 0;
        }
        for(int i = 0;i<k;i++){
            windowSum += nums[i];
        }

        int res = windowSum;
        for(int i = k;i< nums.length;i++){
            windowSum += nums[i] - nums[i - k];
            res = Math.max(res,windowSum);
        }
        return (double) res / k;
    }
}

674. 最长连续递增序列

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

连续递增的子序列 可以由两个下标 lrl < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

示例 1:

输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。 

示例 2:

输入:nums = [2,2,2,2,2]
输出:1
解释:最长连续递增序列是 [2], 长度为1。

提示:

  • 1 <= nums.length <= 104
  • -109 <= nums[i] <= 109
题解

有两种,其实思想都差不多,一个使用一个变量来记录当前递增区间值,一个则是维护左右区间而已

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        // int maxLength = 1;
        // int left = 0,right = 1;
        // int tempLength = 1;
        // for(;right
        //     if(nums[right] > nums[right-1]){
        //         tempLength++;
        //         if(right == nums.length - 1){
        //             maxLength = Math.max(maxLength,tempLength);
        //         }
        //     }else{
        //         maxLength = Math.max(maxLength,tempLength);
        //         tempLength = 1;
        //     }
        // }
        // return maxLength;
        int left = 0, right = 0;
        int res = 0;
        while( right < nums.length){
            if(right > 0&& nums[right-1] >= nums[right]){
                left = right;
            }
            right ++;
            res = Math.max(res,right - left);
        }
        return res;
    }
}

可以点击链接:我正在「编程导航」和朋友们讨论有趣的话题,你⼀起来吧?

也可以加我QQ(2837468248)咨询说明来意!

你可能感兴趣的:(算法学习,算法)