文章目录
算法思想
01 题目关键字
- 主要用来处理连续的问题,输入的是一个数组或者是字符串,求解具有某个性质的子数组或者是子字符串
- 所谓滑动窗口,就是不断调节子序列的起始位置和终止位置,从而得到我们所需要的答案
- 本质上就是双指针算法,用两个指针来维护这个窗口
- 主要有两种类型
- 固定窗口
- 可变窗口:寻找最长 和 寻找最短
02 核心思想
2.1 寻找最长
- 核心:左右双指针位于起始点,右指针向右逐位滑动循环
- 每次滑动过程中
- 如果说,窗内元素满足条件,右指针向右扩大窗口,并更新最优结果
- 如果说,窗内元素不满足条件,左指针向右缩小窗口
- 右指针到达结尾
int left, right, result, bestResult;
while(右指针没有到达结尾){
窗口扩大,加入right对应的元素,更新当前的result;
while(result 不满足 条件) {
窗口缩小,移除left对应的元素,left 右移
}
更新最优解
右指针向右移动
}
2.2 寻找最短
- 核心:左右双指针位于起始点,右指针向右逐位滑动循环
- 每次滑动过程中
- 如果说,窗内元素满足条件,左指针向右缩小窗口,并更新最优结果
- 如果说,窗内元素不满足条件,右指针向右扩大窗口
- 右指针到达结尾
int left, right, result, bestResult;
while(右指针没有到达结尾){
窗口扩大,加入right对应的元素,更新当前的result;
while(result 满足 条件) {
窗口缩小,移除left对应的元素,left 右移
}
更新最优解
右指针向右移动
}
209.长度最小的子数组
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n = nums.length;
if(n == 0) return 0;
int result = Integer.MAX_VALUE;
int left = 0;
int right = 0;
int sum = 0;
while(right < n){
sum += nums[right];
while(sum >= target) {
int len = right - left + 1;
result = result < len ? result : len;
sum -= nums[left++];
}
right ++;
}
if(result == Integer.MAX_VALUE){
return 0;
}
return result;
}
}
1456. 定长子串中元音的最大数量
通过题目进行分析,这个是一个滑动窗口类型的题目。也可以理解为双指针。
- 题目中定义的就是一个长度是 k 的子序列
- 我们用两个指针来维护这个窗口,题目中要求长度是k,所以说我们要求两个指针同时移动来维护窗口固定这个性质。
- 每次移动窗口中都会进行一个新的元素,并且移除一个元素。如果说原来窗口中元音的个数是res,
- 如果说划出去的字符是元音,那么说新窗口中元音的个数就是 res - 1;反之不变
- 如果说新加入的字符是元音,那么说新窗口中元音的个数就是 res + 1;反之不变
- 通过上面的分析,我们可以用 1 来标志元音,用 0 来标志非元音。原来的字符串就等价于01字符串
新窗口中元音的个数 = 原窗口中元音的个数 + 右指针对应的数字 - 左指针对应的数字
class Solution {
public int maxVowels(String s, int k) {
int n = s.length();
if(n == 0 || n < k) return 0;
int[] help = new int[26];
help['a' - 'a'] = 1;
help['e' - 'a'] = 1;
help['i' - 'a'] = 1;
help['o' - 'a'] = 1;
help['u' - 'a'] = 1;
int res = 0;
for(int i = 0; i < k; i++){
if(help[s.charAt(i)-'a'] == 1) res++;
}
int left = 0;
int tem = res;
for(int right = k; right < n; right++){
tem = tem + help[s.charAt(right) - 'a'] - help[s.charAt(left) - 'a'];
res = Math.max(res,tem);
left++;
}
return res;
}
}
剑指Offer. 滑动窗口的最大值
单调队列
本题是单调队列的典型题目。单调队列并不是严格意义上的队列,因为为了维护队列的单调性,队尾允许出队元素。
如果说队列中有两个元素arr[i] >= arr[j] 且 i < j
,无论到什么时候,我们都不会选择arr[i]
作为窗口的最小值,所以说直接将arr[i]
删除。此时队列中剩下的元素,一定是单调递增的。也就说队头的元素是整个队列中的最小值。
为了维护队列的这个性质,在往队列中添加元素的时候,先将队尾大于等于当前数的全部元素进行删除。如果说此时队列的大小超过窗口值,则队头删除元素。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
if(n == 0 || n < k) return new int[]{};
List<Integer> list = new ArrayList<>();
int[] q = new int[n+1];
int hh = 0, tt = -1 ,index =0;
for(int i = 0; i < n; i++){
if(hh <= tt && i - k + 1 > q[hh]) {
hh++;
}
while(hh <= tt && nums[q[tt]] <= nums[i]){
tt--;
}
q[++tt] = i;
if(i >= k-1) {
index++;
list.add(nums[q[hh]]);
}
}
int[] res = new int[index];
for(int i = 0;i < index; i++){
res[i] = list.get(i);
}
return res;
}
}