Sliding window algorithm is used to perform required operation on specific window size of given large buffer or array.滑动窗口算法是在给定特定窗口大小的数组或字符串上执行要求的操作。
This technique shows how a nested for loop in few problems can be converted to single for loop and hence reducing the time complexity.该技术可以将一部分问题中的嵌套循环转变为一个单循环,因此它可以减少时间复杂度。
// 固定窗口大小为 k string s; // 在 s 中寻找窗口大小为 k 时的所包含最大元音字母个数 int right = 0; while(right < s.size()) { window.add(s[right]); // 注:如果放最后,则下面的if改成 right > k right++; // 如果符合要求,说明窗口构造完成, if (right>=k) { // 这是已经是一个窗口了,根据条件做一些事情 // ... 可以计算窗口最大值等 // 最后不要忘记把 right - k 位置元素从窗口里面移除 } } return res;
可以发现此时不需要依赖 left 指针了。因为窗口固定所以其实就没必要使用left。可以直接通过 right 指针来控制窗口。
由于窗口是固定的,因此可以轻易获取到 left 的位置,此处 left = right - k,所以在第二层while中可以通过nums[right-k]来获得left位置得值,从而从窗口中删除。
classSolution{ publicintmaxVowels(String s, int k){ int left = 0, right = 0; int len = s.length(); int maxAns = Integer.MIN_VALUE; int ans = 0; char[] chars = {'a', 'e', 'i', 'u', 'o'}; while (right - left + 1 <= k) { if (contains(chars, s.charAt(right))) { ans++; } right++; } maxAns = Math.max(maxAns, ans);
while (right < len) { if (contains(chars, s.charAt(right))) { ans += 1; } // 被排除的是元音 if (contains(chars, s.charAt(right - k))) { ans -= 1; } maxAns = Math.max(maxAns, ans); right++; }
return maxAns; }
privatebooleancontains(char chars[], char ch){ for (int i = 0; i < chars.length; i++) { if (chars[i] == ch) { returntrue; } } returnfalse; } }
// 更好的做法: classSolution{ publicintmaxVowels(String s, int k){ int right =0; int sum = 0; int max = 0; while (right < s.length()) { sum += isYuan(s.charAt(right)) ; right++; if (right >=k) { max = Math.max(max, sum); sum -= isYuan(s.charAt(right-k)); } } return max; } publicintisYuan(char s){ return s=='a' || s=='e' ||s=='i' ||s=='o' ||s=='u' ? 1:0; } }
classSolution{ publicintlengthOfLongestSubstring(String s){ int len = s.length(); if (len <= 1) { return len; }
int ans = 0; int left = 0, right = 0; HashSet<Character> set = new HashSet<>(); while (right < len) { // 把删除之前s.charAt(right)位置内所有的字符 while (set.contains(s.charAt(right))) { set.remove(s.charAt(left++)); } set.add(s.charAt(right)); ans = Math.max(ans, right - left + 1); right++; } return ans; } }
classSolution{ publicintminSubArrayLen(int target, int[] nums){ int left = 0, right = 0;
int minLen = Integer.MAX_VALUE; int len = nums.length; int sum = 0; while ( right < len) { sum += nums[right]; while (sum >= target) { // 由于需要求最小, 因此这边在left收缩过程中更新minVal minLen = Math.min(minLen, right - left +1); sum -= nums[left]; left ++; } right ++; } return minLen == Integer.MAX_VALUE ? 0 : minLen; } }
classSolution{ publicintlongestOnes(int[] nums, int k){ int ans = 0; int left= 0, right = 0; int len = nums.length; int zeroTimes = 0;
while (right < len) { int now = nums[right]; // 把当前修改成1 if (now == 0) { zeroTimes ++; } // 如果欠了k,则需要从左边进行挪动补偿;保证下一次添加nums[right]时 k一定非负 while( zeroTimes > k) { if (nums[left] == 0) { zeroTimes--; } left ++; } // 获得最大长度 ans = Math.max(ans, right - left + 1); right ++; }
publicdoublefindMaxAverage(int[] nums, int k){ int i = 0; int sum = 0; while (i < k) { sum += nums[i++]; } double maxV = -10000; maxV = Math.max(maxV, (double) sum / k); for (; i < nums.length; i++) { sum = sum - nums[i - k] + nums[i]; maxV = Math.max(maxV, (double) sum / k); } return maxV; }
classSolution{ public List<Integer> findAnagrams(String s, String p){ int len = s.length(); int right = 0; int plen = p.length(); ArrayList<Integer> list = new ArrayList<>(); // 保证p比s短 if (plen > len) { return list; } int[] mp = newint[26]; int[] targetmp = newint[26]; int mpSize = 0; // 记录p中各个字符出现的次数 for (int i = 0; i < plen; i++) { targetmp[ p.charAt(i) - 'a'] ++; }
while (right < len) { char c = s.charAt(right); mp[c-'a']++; mpSize++; // while和if效果是一样的,因为mp大小是通过right一次次增大的 if (mpSize > plen) { // 缩小左边界 char removeChar = s.charAt(right - plen); mpSize--; mp[removeChar-'a']--; } if (right(mp, targetmp)) { list.add(right - plen + 1); } right ++; }
return list; }
privatebooleanright(int[] chars1, int[] chars2){ for (int i = 0; i < chars1.length; i++) { if (chars1[i] != chars2[i]) { returnfalse; } } returntrue; }
classSolution{ publicintequalSubstring(String s, String t, int maxCost){ int len = s.length(); int left = 0, right = 0; int ans = 0; // 现在已经花费的cost int cost = 0; while (right < len) { cost += Math.abs(s.charAt(right) - t.charAt(right)); // 此时cost不满足条件 while (cost > maxCost) { // 通过移动left, 将cost补偿回来 cost -= Math.abs(s.charAt(left) - t.charAt(left)); left++; } // 求最大长度,则更新在第一层while里面 ans = Math.max(ans, right - left + 1); right ++; } return ans; } }
classSolution{ publicint[] maxSlidingWindow(int[] nums, int k) { int len = nums.length; int[] ans = newint[len - k + 1]; int right = 0; int idx = 0; LinkedList<Integer> list = new LinkedList<>();
while (right < len) { int now = nums[right]; // 保持list递减 while (!list.isEmpty() && list.peekLast() < now) { list.pollLast(); } list.addLast(now); right++; if (right >= k) { ans[idx++] = list.peekFirst(); // 下一次的时候将不会再在窗口里面, 所以判断是否出局 if (list.peekFirst() == nums[right - k]) { list.removeFirst(); } } } return ans; } }