leetcode
leetcode 501 ~ 550
和为 K 的子数组

和为 K 的子数组

难度:

标签:

题目描述

代码结果

运行时间: 44 ms, 内存: 18.7 MB


/*
 * Java Stream API 并不适合这个问题的高效解决方案。
 * 因为我们需要处理前缀和及其频率,这在 Stream 中不容易实现。
 * 然而,我们可以用 Stream API 来实现一些辅助功能。
 */
import java.util.HashMap;
import java.util.stream.IntStream;
 
public class StreamSolution {
    public int subarraySum(int[] nums, int k) {
        HashMap<Integer, Integer> prefixSumCount = new HashMap<>();
        prefixSumCount.put(0, 1);
        int[] sum = {0};
        int[] count = {0};
        IntStream.of(nums).forEach(num -> {
            sum[0] += num;
            count[0] += prefixSumCount.getOrDefault(sum[0] - k, 0);
            prefixSumCount.put(sum[0], prefixSumCount.getOrDefault(sum[0], 0) + 1);
        });
        return count[0];
    }
}

解释

方法:

这个题解使用了前缀和的思路。我们维护一个哈希表 sum_set,其中 key 为前缀和,value 为该前缀和出现的次数。我们遍历数组,对于当前遍历到的元素 i,我们将其加到 sum 中,sum 表示当前的前缀和。此时我们查询哈希表,看是否存在 key 为 sum-k 的元素,如果存在,说明从某个位置到当前位置的子数组和为 k,我们将 sum-k 出现的次数加到结果 res 中。最后我们将当前的前缀和 sum 加入哈希表中,如果哈希表中已经存在 sum,则将其出现次数加 1,否则将其加入哈希表,次数为 1。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在这个算法中,前缀和的概念是怎样帮助计算子数组和的?能详细解释前缀和与子数组和之间的关系吗?
前缀和是一个数组的所有元素的累加和,计算到当前元素为止。在这个算法中,通过维护一个前缀和来帮助快速计算任意子数组的和。具体来说,假设我们有一个前缀和 sum[i],它代表了从数组的开始到第 i 个元素的和。如果我们想计算从第 j 到第 i 的子数组的和(其中 j <= i),我们可以使用 sum[i] - sum[j-1] 来得到这个子数组的和。因此,通过查看已存储的前缀和,我们可以快速地计算出任意子数组的和,而无需重复遍历子数组。
🦆
为什么在初始化哈希表时,要先设置哈希表中键0的值为1?这个操作在算法中起到了什么作用?
在哈希表中设置键0的值为1是为了处理那些从数组开头到当前元素累加和正好为 k 的情况。此时,sum-k = 0,我们需要在哈希表中查找键为0的情况。如果不预先设置键0的值为1,那么在算法初始阶段,尽管可能存在一个有效的从数组起始到某点的和为k的子数组,我们也无法统计这种情况。因此,将0的值设置为1是为了正确处理累加和从开始就等于k的子数组。
🦆
哈希表sum_set中存储的键值对是什么意义,键和值分别代表了什么?
在哈希表 sum_set 中,每个键代表一个前缀和,而值代表这个前缀和出现的次数。这样的设计允许算法快速查找在之前的数组元素中有多少次累加和等于当前 sum-k 的情况。每当这种情况出现时,这意味着从某个位置到当前位置的子数组之和为 k。通过记录每个前缀和出现的次数,我们可以快速计算出有多少子数组的和为 k。
🦆
算法在处理负数或零的`nums`元素时的行为会有什么不同?这对最终结果有何影响?
算法处理包含负数或零的元素时的基本逻辑与处理全正数数组相同。前缀和的计算仍然是连续元素的累加,但前缀和的增减会受到元素值正负的影响。例如,负数会导致前缀和减少,而零不会改变前缀和。这种变化影响的是在哈希表中查找 sum-k 的机会。具体来说,负数可能导致更频繁的变化,从而增加找到匹配前缀和的机会,而零的存在则保持前缀和不变,可能在连续多个零的段中不改变子数组的数量。因此,不同的元素值(正数、负数、零)影响前缀和的变化,从而间接影响到统计和为k的子数组的数量。

相关问题

两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

 

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

 

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

 

进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?

连续的子数组和

给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:

  • 子数组大小 至少为 2 ,且
  • 子数组元素总和为 k 的倍数。

如果存在,返回 true ;否则,返回 false

如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 xk 的一个倍数。0 始终视为 k 的一个倍数。

 

示例 1:

输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。

示例 2:

输入:nums = [23,2,6,4,7], k = 6
输出:true
解释:[23, 2, 6, 4, 7] 是大小为 5 的子数组,并且和为 42 。 
42 是 6 的倍数,因为 42 = 7 * 6 且 7 是一个整数。

示例 3:

输入:nums = [23,2,6,4,7], k = 13
输出:false

 

提示:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 109
  • 0 <= sum(nums[i]) <= 231 - 1
  • 1 <= k <= 231 - 1

乘积小于 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

 

示例 1:

输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。

示例 2:

输入:nums = [1,2,3], k = 0
输出:0

 

提示: 

  • 1 <= nums.length <= 3 * 104
  • 1 <= nums[i] <= 1000
  • 0 <= k <= 106

寻找数组的中心下标

给你一个整数数组 nums ,请计算数组的 中心下标

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1

 

示例 1:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

示例 2:

输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。

示例 3:

输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0 。
左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。

 

提示:

  • 1 <= nums.length <= 104
  • -1000 <= nums[i] <= 1000

 

注意:本题与主站 1991 题相同:https://leetcode-cn.com/problems/find-the-middle-index-in-array/

和可被 K 整除的子数组

给定一个整数数组 nums 和一个整数 k ,返回其中元素之和可被 k 整除的(连续、非空) 子数组 的数目。

子数组 是数组的 连续 部分。

 

示例 1:

输入:nums = [4,5,0,-2,-3,1], k = 5
输出:7
解释:
有 7 个子数组满足其元素之和可被 k = 5 整除:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]

示例 2:

输入: nums = [5], k = 9
输出: 0

 

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • 2 <= k <= 104