基本思想
滑动窗口是双指针技巧的一种,定义两个指针,left 和 right,用 left,right 表示滑动窗口的左边界和右边界,通过改变left,right来扩展和收缩滑动窗口,可以想象成一个窗口在滑动。这就是滑动窗口的基本思想
使用滑动窗口前需要思考以下4个点:
- 增大窗口时,要更新哪些数据?
- 什么时候停止增大窗口,开始缩小窗口?
- 缩小窗口时,要更新哪些数据?
- 最后的结果在增大窗口时更新,还是缩小窗口时更新?
写滑动窗口算法前一定要明白以上4个点,不然根本写不出算法!!!
算法模板
抽象算法框架,具体还是得根据题干要求,慢慢补充里面的血肉之躯。
int left = 0, right = 0;
while (right < s.size()) {
window.add(s[right]);
right++;
while (满足缩小窗口条件) {
window.remove(s[left]);
left++;
}
}
一定要明白,滑动窗口算法在写代码中,代码是符合对称结构的。
应用场景
在子串和子数组中应用特别广泛:
- 1、字符串。在一个字符串中找出符合另一个字符串的子串
- 2、子数组。在一个数组中找到满足条件的子数组
例题应用
子串
LeetCode 3题 - 无重复字符的最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
int left = 0;
int right = 0;
int res = 0;
HashMap<Character,Integer> window = new HashMap<>();
while (right < s.length()) {
char c = s.charAt(right);
window.put(c,window.getOrDefault(c,0)+1);
right++;
while(window.get(c)>1){
char c1 = s.charAt(left);
window.put(c1,window.get(c1)-1);
left++;
}
res = Math.max(right-left,res);
}
return res;
}
}
LeetCode 76题 - 最小覆盖子串
class Solution {
public String minWindow(String s, String t) {
if (s.length() < t.length()) {
return "";
}
HashMap<Character,Integer> window = new HashMap<>();
HashMap<Character,Integer> need = new HashMap<>();
for (char c : t.toCharArray()) {
need.put(c,need.getOrDefault(c,0)+1);
}
int left = 0;
int right = 0;
int start = 0;
int end = 0;
int minLen = Integer.MAX_VALUE;
int match = 0;
while (right < s.length()) {
char c = s.charAt(right);
window.put(c,window.getOrDefault(c,0)+1);
if((window.get(c)).equals(need.get(c))){
match++;
}
right++;
while(match==need.size()){
if(right-left<minLen){
start = left;
end = right;
minLen = right-left;
}
char c1 = s.charAt(left);
window.put(c1,window.get(c1)-1);
if(need.containsKey(c1)){
if(window.get(c1)<need.get(c1)){
match--;
}
}
left++;
}
}
return minLen==Integer.MAX_VALUE?"":s.substring(start, end);
}
}
LeetCode 438题 - 找到字符串中所有字母异位词
class Solution {
public List<Integer> findAnagrams(String s, String p) {
HashMap<Character,Integer> window = new HashMap<>();
HashMap<Character,Integer> need = new HashMap<>();
for (char c : p.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0;
int right = 0;
int match = 0;
List<Integer> res = new ArrayList<>();
while (right < s.length()) {
char c = s.charAt(right);
window.put(c, window.getOrDefault(c, 0) + 1);
if(need.containsKey(c)){
if(window.get(c).equals(need.get(c))){
match++;
}
}
right++;
while (match == need.size()) {
if (right - left == p.length()) {
res.add(left);
}
char c1 = s.charAt(left);
window.put(c1,window.get(c1)-1);
if (need.containsKey(c1)) {
if (window.get(c1) < need.get(c1)) {
match--;
}
}
left++;
}
}
return res;
}
}
LeetCode 567题 - 字符串的排列
class Solution {
public boolean checkInclusion(String s1, String s2) {
HashMap<Character,Integer> window = new HashMap<>();
HashMap<Character,Integer> need = new HashMap<>();
for (char c : s1.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0;
int right = 0;
int match = 0;
while (right < s2.length()) {
char c = s2.charAt(right);
window.put(c, window.getOrDefault(c, 0) + 1);
if (need.containsKey(c)) {
if(window.get(c).equals(need.get(c))){
match++;
}
}
right++;
while (match == need.size()) {
if (right - left == s1.length()) {
return true;
}
char c1 = s2.charAt(left);
window.put(c1, window.get(c1) - 1);
if(need.containsKey(c1)){
if (window.get(c1) < need.get(c1)) {
match--;
}
}
left++;
}
}
return false;
}
}
子数组
LeetCode 209题 - 长度最小的子数组
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int right = 0;
int sum = 0;
int minLen = Integer.MAX_VALUE;
while (right < nums.length) {
int num = nums[right];
sum += num;
right++;
while (sum >= target) {
if (right - left < minLen) {
minLen = right - left;
}
int num1 = nums[left];
sum -= num1;
left++;
}
}
return minLen==Integer.MAX_VALUE?0:minLen;
}
}
剑指offer - 和为S的连续正数序列
class Solution {
public int[][] findContinuousSequence(int target) {
List<int[]> res = new ArrayList<>();
int left = 1;
int right = 1;
int sum = 0;
while (right <= (target / 2 + 1)) {
int num = right;
sum += right;
right++;
while (sum >= target) {
if (sum == target) {
int[] temp = new int[right - left];
for (int i = 0; i < temp.length; i++) {
temp[i] = left + i;
}
res.add(temp);
}
int num1 = left;
sum -= num1;
left++;
}
}
return res.toArray(new int[res.size()][]);
}
}
参考资料
我写了套框架,把滑动窗口算法变成了默写题 滑动窗口算法框架
|