滑动窗口
209 长度最小的子数组
https://leetcode-cn.com/problems/minimum-size-subarray-sum/submissions/
首先是滑动窗口的思路,我感觉这种寻找满足某个条件的连续的子集,如这里的数组,可能还会在字符串里用到。
但这题卡了我很久的是边界条件,下面这段代码,当循环退出的时候,right的值是没有被加到sum里的,所以right指向的位置是在sum数组的后面一个,所以最后计算结果的时候不是right-left+1,而是right-left,原因就是right在sum后面。
while (sum<s && right<nums.length){ sum+=nums[right]; right++; }
其次是初始条件如何设置,我一开是设置的left=right=0,这么设置是可以的,但是我把sum设置为了nums[0],这样就有问题了,因为循环里面的时候sum+=nums[0],如果开头sum=nums[0]这样同一个nums[0]加了两次。
class Solution { public int minSubArrayLen(int s, int[] nums) { if(nums.length==0){ return 0; } int left=0; int right = 0; int sum=0; int result = Integer.MAX_VALUE; while (left<=right && right<nums.length){ while (sum<s && right<nums.length){ sum+=nums[right]; right++; } while (sum>=s && left<right&& left<nums.length){ result = (result>(right-left))?right-left:result; sum-=nums[left]; left++; } } return result==Integer.MAX_VALUE?0:result; } }
219 存在重复元素2
https://leetcode-cn.com/problems/contains-duplicate-ii/
要求从长度为k的子元素串,变成了长度最大为k,其实本质上是一样的。从索引为0的元素开始,检查该元素长度为k的窗口是否满足条件,如果满足则返回,不满足则继续。在这一题中我想到了一个小小的优化,我之前的做法是先根据窗口的长度判断i的最大值,即nums.length-i-k,但我发现这样没有必要,且不符合题目中要求的最大为k。所以我把i是否越界放到j的循环里判断,用j<nums.length来避免越界。
class Solution { public boolean containsNearbyDuplicate(int[] nums, int k) { for (int i=0;i<nums.length;i++){ //在长度为k的区间内检查是否能找到与i相等的点 for (int j=i+1;j<=i+k && j<nums.length;j++){ if (nums[j]==nums[i]){ return true; } } } return false; } }
除此之外还有一种优化手法,用空间换时间,用一个HashMap存储窗口里的数值,如果每次滑动的时候删除一个数值并加入一个新的数值。最终的提交结果是优于上一种方法。
- 首先窗口的长度为k+1,因为要求窗口里的元素索引之差为k。所以写和滑动窗口有关的题目第一步计算出窗口的长度
- 其次从窗口的左端遍历窗口的时候要在循环条件里加上不越界的判断,即第一个循环的i<nums.length
class Solution { public boolean containsNearbyDuplicate(int[] nums, int k) { HashSet<Object> set = new HashSet<>(); //从0开始的前k个元素放进去 for (int i=0;i<k+1&&i<nums.length;i++){ if (!set.add(nums[i])){ return true; } } int i=0; for (int j=k+1;j<nums.length;j++){ set.remove(nums[i]); if (!set.add(nums[j])){ return true; } i++; } return false; } }