目录
560. 和为K的子数组
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
看到这道题首先想到的是滑动窗口(双指针)感觉似曾相识,然后失败了。因为会出现负数的情况。
下面和可以用之前双指针思路的题目对比一下
滑动窗口
面试题57. 和为s的两个数字
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
划重点:递增排序
思路是双指针 i , j 分别指向数组 nums的左右两端,然后往里面移动
代码
class Solution {
public int[] twoSum(int[] nums, int target) {
if(nums == null){
return new int[0];
}
int i = 0;
int j = nums.length-1;
while(i < j){
int sum = nums[i] + nums[j];
if(sum == target){
return new int[]{
nums[i], nums[j]};
}else if(sum < target){
i++;
}else{
j--;
}
}
return new int[0];
}
}
面试题57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
划重点:正整数序列 从小到大排列
思路是用i和j两个指针,然后分别往右移动,如果和小于target,则j++,如果和大于target,则i++
代码
class Solution {
public int[][] findContinuousSequence(int target) {
int i = 1;
int j = 2;
int sum = 3;
List<int[]> res = new ArrayList<>();
while(i <= target / 2){
if(sum == target){
int[] ans = new int[j-i+1];
int k = i;
for(int m = 0; m < j-i+1; m++){
ans[m] = k;
k++;
}
res.add(ans);
sum -= i;
i++;
j++;
sum += j;
}else if(sum < target){
j++;
sum += j;
}else{
sum -= i;
i++;
}
}
return res.toArray(new int[res.size()][]);
}
}
前缀和 + 哈希表
然后回到560. 和为K的子数组这道题,没有说从小到大,也不是正整数。不能用滑动窗口的思路,需要用到前缀和的思想。
- 前缀和:当前项之前的所有项的和,则可以将当前项用两个相邻的前缀和的差来表示
preSum[x] = nums[0]+nums[1]+…+nums[x] - nums 数组从i到 j项的和:
nums[i] +…+nums[j]=preSum[j] - preSum[i-1] =k
即preSum[j] =preSum[i-1] -k - 所以转化成
找前缀和数组中两个数的差==k的情况
。 - 因为会有正负数,就会抵消,存在前缀和相等的情况(前缀和数组中会有重复),所以用哈希表来存这个前缀和数组。
代码
class Solution {
public int subarraySum(int[] nums, int k) {
// key:前缀和,value:key 对应的前缀和的个数
Map<Integer, Integer> preSumFreq = new HashMap<>();
int count = 0;
int preSum = 0; //前缀和
preSumFreq.put(0, 1);
for(int num : nums){
preSum += num;
// 先获得前缀和为 preSum - k 的个数,加到计数变量里
if(preSumFreq.containsKey(preSum - k)){
count += preSumFreq.get(preSum - k);
}
// 维护 preSumFreq
preSumFreq.put(preSum, preSumFreq.getOrDefault(preSum, 0) + 1);
}
return count;
}
}
- HashMap中的key:当前前缀和;value:当前前缀和出现的次数
- 每次遍历一个数字,计算前缀和,放入map中,如果之前没出现过这个前缀和,则value为1,如果出现过,则value为之前的次数+1
- 如果发现map 中存在 key 为
当前前缀和 - k
,说明存在 【之前的某个前缀和】,满足 【当前前缀和】 - 【之前的某个前缀和】 === k,把对应的次数累加到count中。看【之前的某个前缀和】出现几次,count就加几次。 - 其中map.getOrDefault的用法:在这题中,当Map中有这个key时,就使用这个key对应的value值,如果没有就使用默认值0。
1248. 统计「优美子数组」
给你一个整数数组 nums 和一个整数 k。
如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。
请返回这个数组中「优美子数组」的数目。
前缀和+哈希表
和前面NO.560的思路相同,不同的是这里的前缀和不再是真正的前i个数字的和,而是要把前缀和想象成当前的奇数个数。
class Solution {
public int numberOfSubarrays(int[] nums, int k) {
Map<Integer, Integer> preFixCnt = new HashMap<>();
//key是前缀和(当前奇数的个数),value是前缀和的个数
preFixCnt.put(0, 1);
int count = 0;
int sum = 0;