前言
滑动窗口的一般套路是:
维护两个指针,一左一右,代表窗口的两个边界。
左右边界只能向左不能向右。
按要求在右边界填加数组新元素
在一定情况下,移除左边界的元素【即左指针左移】
维护窗口中值保持某种性质
大部分情况下都适用双端队列Deque
来实现
59是滑动窗口最经典的题目。 滑动窗口分为固定与变长,这题属于固定窗口。
本题还可以顺便练习以下双端队列Deque的API【以前我一直以为只有LinkedList才有队列,原来还有一个接口】
57题是变长滑动窗口题。
一、59 滑动窗口的最大值
维护一个双端队列,保证输入元素的单调递减。
每次插入一个元素,将最左元素除去
注意:若最左元素是窗口外元素,需要去除掉
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length < k) return new int[0];
// 因为k总是有效的,不必考虑k是否大于范围
int len = nums.length;
if (len == 0) return new int[0];
int[] res = new int[len - k + 1];
Deque<Integer> deque = new LinkedList<>();
//将第一趟的结果存入res[0]
for (int i = 0; i < k; i++) {
while (!deque.isEmpty() && deque.peekLast() < nums[i]) deque.pollLast();
deque.offerLast(nums[i]);
}
res[0] = deque.peekFirst();
for (int i = k; i < len; i++) {
if (deque.peekFirst() == nums[i - k]) deque.pollFirst();
while (!deque.isEmpty() && deque.peekLast() < nums[i]) deque.pollLast();
deque.offerLast(nums[i]);
//注意原数组与结果数组的下标对应关系
res[i - k + 1] = deque.peekFirst();
}
return res;
}
二、57 连续子序列和为某一个值
维护左右指针,和一个sum,若窗口值总和sum < target, 右指针右移,直到sum >= target 若sum > target ,将左指针右移,并不断减少sum的对应值 若sum == target, 判断队长是不是1,将结果添加进入【**并将最左元素移除**】 public int[][] findContinuousSequence(int target) {
if(target == 1) return new int[0][0];
List<List<Integer>> res = new ArrayList<>();
for (int i = 1; i < target / 2 + 1; i ++) {
int sum = 0;
for (int j = i; j < target / 2 + 2; j++ ) {
sum += j;
if (sum == target) {
List<Integer> list = new ArrayList<>();
for (int k = i; k <= j; k++) {
list.add(k);
}
res.add(list);
}
if (sum > target) break;
}
}
int[][] result = new int[res.size()][];
for (int i = 0; i < res.size(); i++) {
result[i] = res.get(i).stream().mapToInt(Integer::valueOf).toArray();
}
return result;
}
三、58 翻转字符串
从右向左定义一个窗口,使得其中的每个字符不为空
这题的关键在于使用substring()
public String reverseWords(String s) {
if (s == null || s.trim().isEmpty()) return "";
s = s.trim();
int hi = s.length() - 1, lo = hi;
StringBuilder sb = new StringBuilder();
List<String> res = new ArrayList<>();
while (hi >= 0) {
while (s.charAt(hi) == ' ') hi--;
lo = hi;
while (lo >= 0 && s.charAt(lo) != ' ') lo--;
sb.append(s.substring(lo + 1, hi + 1) + " ");
hi = lo;
}
return sb.toString().trim();
}
总结
有序数组需要条件反射式的想到三种解法:
(1)二分;
(2)左右双指针;
(3)滑动窗口。
有“最大值”不一定是动态规划嗷!