饕餮盛宴——滑动窗口(尺取法)
注意:此章节作为复习使用,直接代码实现,不写思路。
LeetCode1004
代码实现
class Solution {
public int longestOnes(int[] A, int K) {
int left = 0;
int right = 0;
int len = 0;
// 0计数器
int K1= 0;
int nums = 0;
while (right < A.length) {
if (A[right] == 1) {
nums++;
}
else {
K1++;
}
right++;
while (K1 > K) {
if (A[left] == 1) {
nums--;
}
else {
K1--;
}
left++;
}
len = Math.max(right - left, len);
}
return len;
}
}
LeetCode239
代码实现
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int left = 0;
int[] window = new int[nums.length - k + 1];
int j = 0;
int right = k;
while (right <= nums.length) {
int maxNum = nums[left];
for (int i = left; i < right; i++) {
maxNum = Math.max(maxNum, nums[i]);
}
window[j] = maxNum;
right++;
left++;
j++;
}
return window;
}
}
// 如果需要在自己的IDE上输出,则只需要调用Arrays.toString()方法即可。
// PS:目前这种方法似乎力扣提交显示超时,后面方法多了再说,毕竟是自己手工做的o( ̄▽ ̄)d
LeetCode424
代码实现
class Solution {
public int characterReplacement(String s, int k) {
// 建立哈希表存储每个字母出现的次数
HashMap<Character, Integer> record = new HashMap<>();
// 初始化滑动窗口
int left = 0;
int right = 0;
int len = 0;
int num = 0;
while (right < s.length()) {
char c = s.charAt(right);
right++;
// 初始化哈希表
record.put(c, record.getOrDefault(c, 0) + 1);
// 找到哈希表中出现次数最多的字母的值
num = Math.max(num, record.get(c));
len = right - left;
// 收缩窗口的时机:当出现次数加上可以替换的次数大于窗口长度时需要收缩窗口
while (num + k < len) {
char c1 = s.charAt(left);
left++;
len = right - left;
// 更新数据
record.put(c1, record.get(c1) - 1);
}
}
if (num + k > s.length()) {
return s.length();
}
return num + k;
}
}
LeetCode480
代码实现
class Solution {
public double[] medianSlidingWindow(int[] nums, int k) {
int left = 0;
int right = k;
int i = 0;
double[] result = new double[nums.length - k + 1];
int[] median = new int[k];
while (right <= nums.length) {
double medianNum = 0.0;
System.arraycopy(nums, left, median, 0, k);
Arrays.sort(median);
if (k % 2 == 0) {
// 这里强制类型转换是为了防止数组元素超过int型范围
medianNum = (double)((long)median[k / 2] + (long)median[k / 2 - 1]) / 2;
}
else {
medianNum = median[k / 2];
}
result[i] = medianNum;
right++;
left++;
i++;
}
return result;
}
}
这道题暴力加滑动窗口思想解决,效率低下,暂时就这样。
LeetCode992
代码实现
class Solution {
public int subarraysWithKDistinct(int[] A, int K) {
return atMostKDistinct(A, K) - atMostKDistinct(A, K - 1);
}
/**
* @param A
* @param K
* @return 最多包含 K 个不同整数的子区间的个数
*/
private int atMostKDistinct(int[] A, int K) {
int left = 0;
int right = 0;
int result = 0;
HashMap<Integer, Integer> window = new HashMap<>();
while (right < A.length) {
int a = A[right];
window.put(a, window.getOrDefault(a, 0) + 1);
right++;
while (window.size() > K) {
int b = A[left];
left++;
window.put(b, window.get(b) - 1);
if (window.get(b) == 0) {
window.remove(b);
}
}
result += right - left;
}
return result;
}
}
问:为什么可以用right - left 来表示每次新增子数组?
答:举例说明,[A,B,C]变为[A,B,C,D]新增子数组有[D],[C,D],[B,C,D],[A,B,C,D]。即新数组长度。
注意:遇见恰好转换为最值问题比较适合用滑动窗口。
LeetCode713
与992类似。不过更加简单,理解思路很重要。直接写代码~
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int left = 0;
int right = 0;
int mul = 1;
int result = 0;
while (right < nums.length) {
int numR = nums[right];
mul *= numR;
right++;
while (mul >= k) {
// 注意对特殊情况的处理
if (k <= 1) {
return 0;
}
int numL = nums[left];
left++;
mul /= numL;
}
// 关键点
result += right - left;
}
return result;
}
}
LeetCode904
代码实现
class Solution {
public int totalFruit(int[] tree) {
int left = 0;
int right = 0;
int result = 0;
HashMap<Integer,Integer> window = new HashMap<>();
while (right < tree.length) {
int a = tree[right];
right++;
window.put(a, window.getOrDefault(a, 0) + 1);
while (window.size() > 2) {
int b = tree[left];
left++;
window.put(b, window.get(b) - 1);
if (window.get(b) == 0) {
window.remove(b);
}
}
// 每次保留窗口长度最大值
result = Math.max(result, right - left);
}
return result;
}
}
LeetCode1358
代码实现
class Solution {
public int numberOfSubstrings(String s) {
if (s.length() < 3) {
return 0;
}
if (s == null) {
return 0;
}
int left = 0;
int right = 0;
int res = 0;
HashMap<Character, Integer> window = new HashMap<>();
while (right < s.length()) {
char ch = s.charAt(right);
right++;
window.put(ch, window.getOrDefault(ch, 0) + 1);
while (window.getOrDefault('a', 0) > 0 && window.getOrDefault('b', 0) > 0 && window.getOrDefault('c', 0) > 0) {
char ch1 = s.charAt(left);
res += s.length() - right + 1;
left++;
window.put(ch1, window.get(ch1) - 1);
}
}
return res;
}
}
当然,由于数据量不大,也可以考虑用数组记录结果就行。代码如下:
class Solution {
public int numberOfSubstrings(String s) {
if (s.length() < 3) {
return 0;
}
if (s == null) {
return 0;
}
int left = 0;
int right = 0;
int res = 0;
int[] ans = new int[s.length()];
while (right < s.length()) {
char ch = s.charAt(right);
right++;
ans[ch - 'a']++;
while (ans[0] > 0 && ans[1] > 0 && ans[2] > 0) {
char ch1 = s.charAt(left);
res += s.length() - right + 1;
left++;
ans[ch1 - 'a']--;
}
}
return res;
}
}
未完待续,还有不少类似的题目,先到这里,融会贯通,掌握思想最重要。