Leetcode剑指offer刷题day02
主要内容,双指针+滑动窗口
题目一描述:
给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
题目分析:
暴力解法,直接枚举所有的连续子数组,判断其是否满足要求,时间复杂度为O(n^2)。
双指针+滑动窗口的思想也不难想到,观察题目描述要求的连续子数组,因此考虑当我们找到一个连续子数组,nums[i],nums[i+1]…nums[j],它们的和大于目标数target,那么此时我们可以固定窗口的右边界,尝试缩短左边界,判断是否缩短之后也满足要求,一直缩短,知道当前窗口的和值小于target,才考虑移动右边界。(这里可以理解为找到以nums[j]为结尾的连续子数组)。因此解题思路:定义两个指针l和r,初始时指针都指向下标为0的位置,在每一次迭代的过程中加上nums[r]的值,当累加和sum>=target时,就开始考虑缩短左边界了,缩短左边界后不满足要求了,就开始移动右边界,终止条件为右边界出界。
代码实现:
//双指针+滑动窗口,求最小连续子数组的和,满足和>=target
public int minSubArrayLen(int target, int[] nums) {
int n=nums.length;
int l=0;
int r=0;
int sum=0;
int res=Integer.MAX_VALUE;
while (r<n){
sum+=nums[r];
while (sum>=target){
res=Math.min(res,r-l+1);
sum-=nums[l];
l++;
}
r++;
}
return res==Integer.MAX_VALUE?0:res;
}
注意,滑动窗口需要注意的地方是当前维护的窗口是 l…r之间的数,l==r时当前窗口还维持着一个数,并非没有数,终止条件一般为右指针出界。
该题可以用双指针+滑动窗口,在于nums数组中的每个数是大于0的。否则使用滑动窗口的思想会出错。
题目二描述:
给定一个正整数数组 nums
和整数 k
,请找出该数组内乘积小于 k
的连续的子数组的个数。
题目分析:
在分析了上一道题连续子数组的和,这一道题则是连续子数组的积。我们还是考虑对于一个连续的子数组nums[i],nums[i+1]…nums[j-1],如果nums[j]加到该连续子数组中,使得它们的积小于k,那么对于加进来的nums[j],可以使原来的满足条件的方案数加上j-i+1种(这里依然可以理解为加上以nums[j]为结尾的满足条件的连续子数组的个数)。这里需要注意的是如果当个数就大于target,就要考虑从该数的下一个数重新开始,累计积重置为1,两个指针置于同一个位置。当当前窗口中的所有数的累积大于等于k时,就要考虑固定右边界,缩小左边界,直到当前窗口的大小为0或者右边界的下一个数能够加到当前窗口中(计算累积小于k时)。
代码实现:
//滑动窗口的解决办法,双指针
public int numSubarrayProductLessThanK(int[] nums, int k) {
int n = nums.length;
int l=0;
int r=0;
int res=0;
int ans=1;
while (r<n){
while (r<n&&ans*nums[r]<k){
ans=ans*nums[r];
res+=r-l+1;
r++;
}
//如果单个数比k还要大的话,就要考虑从下一个数重新开始
if(l==r){
ans=1;
l++;
r=l;
}else {
ans/=nums[l];
l++;
}
}
return res;
}