20.<tag-数组和前缀和(并用哈希表优化)> lt.560-和为 K 的子数组 0.8

lt.560-和为 K 的子数组

[案例需求]
在这里插入图片描述
[思路分析一, 暴力解法: 双重for循环(固定一个边界)]

固定了起点,即先固定左边界,然后枚举右边界.

  • 外层循环表示start不断的靠近数组末尾,
  • 内层循环表示不断从end到0倒着连加, 把每次==k的数, 在计数器count上+1;
    [代码实现]
public class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0;
        for (int start = 0; start < nums.length; ++start) {
            int sum = 0;
            for (int end = start; end >= 0; --end) {
                sum += nums[end];
                if (sum == k) {//找到目标值了. 为什么还不跳出内循环, 因为数组还有负数呢!sum也有可能继续==k
                    count++; 
                }
            }
        }
        return count;
    }
}

[思路分析二, 前缀和 + 双重for循环]

在求区间和时, 我们可以用前缀和来降低反复的调用或者使用前缀和带来的复杂度.
本思路先把数组的前缀和求出来并保存在preSum数组中, 再使用双重for循环遍历前缀和数组, 找到preSum[j] - preSum[i] == k的所有可能的组合.

[代码实现]

//1. 前缀和 + 双重for循环
class Solution {
    public int subarraySum(int[] nums, int k) {
        //前缀和求法
        //首先求出前缀和数组
        int len = nums.length;
        int[] preSum = new int[len + 1]; //前缀和数组从0开始, 所以比原始数组大1

        for(int i = 1; i <= len; i++){
            preSum[i] = preSum[i - 1] + nums[i - 1]; // 从preSum[0], nums[0] 到 preSum[len]
        }


        //因为和为k的几个数, 数量不确定,  而且这几个数更不一定就是顺序的, 所以 preSum[i] - pre[j] = k, i和j都不是固定的
        // 就需要一个二维的遍历循环, 外层循环遍历i个数的presum, 内层循环遍历 每次preSum[i]时的合适的preSum[j]

        int count = 0;

        for(int i = 0; i < len; i++){
            for(int j = i; j < len; j++){
                //
                //注意偏移,因为我们的nums[2]到nums[4]等于presum[5]-presum[2]
                //所以这样就可以得到nums[i,j]区间内的和
                if(preSum[j + 1] - preSum[i] == k){
                    count++;
                }
            }
        }

        return count;
    }
}

[思路分析三, 使用哈希表优化前缀和]

在这里插入图片描述

[代码实现]

 class Solution {
        public int subarraySum(int[] nums, int k) {
            int[] prefixSum = new int[nums.length + 1];
            prefixSum[0] = 0;
            for (int i = 0; i < nums.length; i++) {
                prefixSum[i + 1] = prefixSum[i] + nums[i];
            }
         
            Map<Integer, Integer> map = new HashMap<>(); // key 为前缀和,value为前缀和为key的个数,问题转化为和为k的问题
            int ans = 0;
            for (int i = 0; i < prefixSum.length; i++) {
                // 如果有与当前prefixSum[i]的差为k的,则加上它的个数
                ans = ans + map.getOrDefault(prefixSum[i] - k, 0);
                // 统计前缀和的个数
                map.put(prefixSum[i], map.getOrDefault(prefixSum[i], 0) + 1);
            }
            return ans;
        }
    }

在这里插入图片描述
在这里插入图片描述

posted @ 2022-05-26 20:29  青松城  阅读(41)  评论(0)    收藏  举报