【数据结构】哈希表&前缀和/滑动窗口双指针 和为k的子数组/两数三数四数之和-LeetCode题型记录(Java)

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;
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值