滑动窗口法
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
以经典题为例:(力扣链接🔗)
暴力解法:
使用两次for循环即可,然后不断的寻找符合条件的子序列,时间复杂度很明显是#card=math&code=O%28n%5E2%29&id=Lg2lP)。
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX; // 最终的结果
int sum = 0; // 子序列的数值之和
int subLength = 0; // 子序列的长度
for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
sum = 0;
for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
sum += nums[j];
if (sum >= s) { // 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
滑动窗口法:
滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将#card=math&code=O%28n%5E2%29&id=dAC46)的暴力解法降为
#card=math&code=O%28n%29&id=bXs6s)。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0, right = 0, sum = 0, result = Integer.MAX_VALUE;
for (; right < nums.length; right++) {
sum += nums[right];
// 如果大于则进行移动滑动窗口
while (sum >= target) {
// 取最小序列的长度
result = Math.min(result, right - left + 1);
// 移动滑动窗口
sum -= nums[left++];
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
}
相关题目
水果成篮
题目描述(力扣链接)
解题方法
使用哈希表来判断是否重复,使用滑动窗口来更新范围。
class Solution {
public int totalFruit(int[] fruits) {
// 使用hashMap判断是否重复
HashMap<Integer, Integer> map = new HashMap<>();
int right = 0, left = 0, result = 0;
for (; right < fruits.length; right++) {
// 将遍历的元素放入map中
map.put(fruits[right], map.getOrDefault(fruits[right], 0) + 1);
// 如果其中map的长度超过三,则滑动窗口
while (map.size() > 2) {
// 将left的取值减一
map.put(fruits[left], map.get(fruits[left]) - 1);
// 如果left的数量为0之后,则直接清除
if (map.get(fruits[left]) == 0) map.remove(fruits[left]);
left++;
}
// 获取其中取得最大的范围
result = Math.max(result, right - left + 1);
}
return result;
}
}
最小覆盖子串
题目描述(力扣链接)
解题方法:
使用2个哈希表分别记录2个字符串中得词频情况进行判断,使用left和right两个指针在左闭右开的区间进行滑动,当包含t中所有的字符时停止滑动,进行判断,更新结果,再继续进行滑动。
class Solution {
public String minWindow(String s, String t) {
// 记录窗口中的词频数量
HashMap<Character, Integer> window = new HashMap<>();
// 记录t中的所有字符,用来查询是否存在
HashMap<Character, Integer> map = new HashMap<>();
// count为s中包含t字符的个数,result为最小字符串的大小, start记录最小字符串开始的位置
int left = 0, right = 0, count = 0, result = Integer.MAX_VALUE, start = 0;
// 将t中的字符串全部放入map中
for (int i = 0; i < t.length(); i++) {
map.put(t.charAt(i), map.getOrDefault(t.charAt(i), 0) + 1);
}
// 循环进行判断
while (right < s.length()) {
// 如果map中含有该字符,将字符的value值加一,并且将count加一
if (map.containsKey(s.charAt(right))) {
window.put(s.charAt(right), window.getOrDefault(s.charAt(right), 0) + 1);
if (map.get(s.charAt(right)).equals(window.get(s.charAt(right)))) {
count++;
}
}
// 移动窗口右侧进行遍历
right++;
// 当计数器等于t的大小时,此时窗口需要移动
while (count == map.size()) {
// 如果窗口大小小于上一个窗口的大小,进行赋值
if (right - left < result) {
result = right - left;
start = left;
}
// 移动窗口
if (map.containsKey(s.charAt(left))) {
// 如果window中的值和map中的值数量相等才是包含t的字串
if (window.get(s.charAt(left)).equals(map.get(s.charAt(left)))) {
count--;
}
// 将窗口中保存值的数量减一
window.put(s.charAt(left), window.getOrDefault(s.charAt(left), 0) - 1);
}
left++;
}
}
return result == Integer.MAX_VALUE ? "" : s.substring(start, start + result);
}
}
固定长度的滑动窗口
找到字符串中所有字母异位词
详细见此链接题三
https://www.yuque.com/jugelizi-rxt6d/dqbihl/nm3gl3