《算法通关村——原来滑动窗口如此简单》
基本思想
滑动窗口的思想非常简单,如下图所示,假如窗口的大小是3,当不断有新数据来时,我们会维护一个大小为3的一个区间,超过3的就将新的放入老的移走。
这个过程有点像火车在铁轨上跑,原始数据可能保存在一个很大的空间里(铁轨),但是我们标记的小区间就像一列长度固定的火车,一直向前走。
有了区间,那我们就可以造题了,例如让你找序列上三个连续数字的最大和是多少,或者子数组平均数是多少(LeetCode643)等等。
从上面的图可以看到,所谓窗口就是建立两个索引,left和right,并且保持{left,right}之间一共有3个元素,然后一边遍历序列,一边寻找,每改变一次就标记一下当前区间的最大值就行了。
这个例子已经告诉我们了什么是窗口、什么是窗口的滑动:
-
窗口:窗口其实就是两个变量left和right之间的元素,也可以理解为一个区间。窗口大小可能固定,也可能变化,如果是固定大小的,那么自然要先确定窗口是否越界,再执行逻辑处理。如果不是固定的,就要先判断是否满足要求,再执行逻辑处理。
-
滑动:说明这个窗口是移动的,事实上移动的仍然是left和right两个变量,而不是序列中的元素。当变量移动的时,其中间的元素必然会发生变化,因此就有了这种不断滑动的效果。
在实际问题中,窗口大小不一定是固定的,我们可以思考两种场景:
- 固定窗口的滑动就是火车行驶这种大小不变的移动 。
- 可变的窗口就像两个老师带着一队学生外出,一个负责开路,一个负责断后,中间则是小朋友。两位老师之间的距离可能有时大有时小,但是整体窗口是不断滑动的。
根据窗口大小是否固定,可以造出两种类型的题:
-
如果是固定的,则一般会让你求哪个窗口的元素最大、最小、平均值、和最大、和最小等等类型的问题。
-
如果窗口是变的,则一般会让你求一个序列里最大、最小窗口是什么等等。
练题
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. 最长连续递增序列
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l
和 r
(l < 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<nums.length;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)咨询说明来意!