3.最大连续1的个数
题目:. - 力扣(LeetCode)
题目要求的是给定一个二进制数组 nums
和一个整数 k
,如果可以翻转最多 k
个 0
,则返回 数组中连续 1
的最大个数 。
按照题目正面去做,还要替换0,很麻烦
反正我们最后要求的是最长子序列
那么我们就用最长子序列的方法来做,把问题转化为求一段最长子序列,其中这个子序列中0的个数不超过K个,那么这个子序列中的0翻转后得到的子序列一定是最长连续1的子序列
那么我们用滑动窗口的题目来做:
(1)进窗口:定义一个计数器来统计当前窗口中0的个数,进窗口时候:如果是0,计数器+1,是1则无视
(2)判断 当前计数器是否大于k,大于则出窗口
(3)出窗口
当right走到如上图所示位置时,就要出窗口,但是出窗口不只是left++这么简单
如果left++,那么left从第二个位置开始走,一开始计数器的值还是大于
k,所以left要一直走,直到计数器的值小于k
题解:
public int longestOnes(int[] nums, int k) { int n = nums.length; int len = 0; for(int left = 0,right = 0,zero = 0; right < n; right++){ if(nums[right] == 0){ zero++; } while(zero > k){ zero -= 1 - nums[left++]; } len = Math.max(len,right - left + 1); } return len; }
4.将x减到0的最小操作数
题目:. - 力扣(LeetCode)
此题从正面去解非常麻烦,因为用左边还是右边去减是无法判断的
我们利用"正难则反"的策略
题目说每次都是从最左边或者最右边拿一个数来给x减掉,直到x为0,求最小的操作数
那么我们可以把题目转化为:求最长的子数列,要求子序列的和为 "数组和 - x"
这样就转化为我们熟悉的题目了
此类题目依旧可以用滑动窗口来解决
(1)进窗口:利用sum来表示当前窗口的元素和,sum += nums[right]
(2)判断:sum > targret ,则出窗口
(3)出窗口:sum -= target,直到sum < target(这里在前几题已说明)
题解:
class Solution {
public int minOperations(int[] nums, int x) {
int n = nums.length;
int min = n + 1;
int s = 0;
for(int t : nums){
s += t;
}
for(int right = 0,left = 0,target = s - x,sum = 0; right < n; right++){
sum += nums[right];
while(sum > target && left <= right){
sum -= nums[left++];
}
if(sum == target){
min = Math.min(min,n - (right - left + 1));
}
}
return min == n + 1 ? -1 : min;
}
}
5.水果成篮
题目:. - 力扣(LeetCode)
题目转化:找一个最长子序列,子序列中的水果类型不超过两种
我们很容易想到暴力解法,即暴力枚举出所有的结果,其中可以利用哈希表检查水果的类型数量
我们可以在暴力枚举的方法上进行优化:
(1)当right到达如图所示的位置时,如果用暴力解法,那么left++后,right要从left的位置开始遍历.但是事实上不必如此.right还是在原来的位置,因为之前的类型中已经记录下right之前的水果类型数目.那么这就是我们的滑动窗口
(2)在出窗口时,不仅仅是left++
如上图:即使left++后,水果类型的数据还是大于2,因此我们应该一直让left++,直到水果类型数目小于等于2
那么其中就要记录下每种水果的数目,每次出窗口都要让当前类型的水果-1,假设某种水果的数量为0,则类型-1
我们可以利用Map的键值对来解决这个问题
题解:
(1)进窗口:每次将fruits[right]的水果数目+1
(2)判断当前水果类型数目是否大于2
(3)出窗口(按照上面的要求)
class Solution {
public int totalFruit(int[] fruits) {
Map<Integer,Integer> type = new HashMap<>();
int count = 0;
int n = fruits.length;
for(int left = 0, right = 0; right < n; right++){
type.put(fruits[right],type.getOrDefault(fruits[right],0)+1);
while(type.size() > 2){
int tmp = fruits[left];
type.put(tmp,type.get(tmp)-1);
if(type.get(tmp) == 0){
type.remove(tmp);
}
left++;
}
count = Math.max(count,right-left+1);
}
return count;
}
}
但是当我们提交时还是会发现时间复杂度偏大,我们看看题目的数据范围:
也就是我们没必要设置一个Map,我们是知道数据的范围的
所以进行优化,引进一个num来代表水果的类型数量
class Solution {
public int totalFruit(int[] fruits) {
int n = fruits.length;
int[] type = new int[n + 1];
int count = 0;
for(int left = 0, right = 0, num = 0; right < n; right++){
if(type[fruits[right]] == 0){
num++;
}
type[fruits[right]]++;
while(num > 2){
int tmp = fruits[left];
type[tmp]--;
if(type[tmp] == 0){
num--;
}
left++;
}
count = Math.max(count,right-left+1);
}
return count;
}
}