查找总和等于“k”的子数组的数量

时间:2017-07-12 19:32:35

标签: java algorithm

我们将得到一个整数数组和一个值k。我们需要找到总和等于k的子数组的总数。

我在网上找到了一些有趣的代码(在Leetcode上),如下所示:

public class Solution {
    public int subarraySum(int[] nums, int k) {
        int sum = 0, result = 0;
        Map<Integer, Integer> preSum = new HashMap<>();
        preSum.put(0, 1);

        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            if (preSum.containsKey(sum - k)) {
                result += preSum.get(sum - k);
            }
            preSum.put(sum, preSum.getOrDefault(sum, 0) + 1);
        }

        return result;
    }
}

为了理解这一点,我浏览了一些具体示例,例如[1,1,1,1,1] k=3[1,2,3,0,3,2,6] k=6。虽然代码在两种情况下都能很好地工作,但我无法理解它实际上如何计算输出。

我有两个具体的混淆点:

1)为什么代码会连续添加数组中的值,而不会将其归零?例如,如果[1,1,1,1,1]k=3sum=3一次,我们是否需要将sum重置为零?不会重置sum干扰查找以后的子阵列吗?

2)当我们找到总和result++的子数组时,我们不应该只是k吗?为什么我们要添加preSum.get(sum-k)

1 个答案:

答案 0 :(得分:11)

让我们先处理你的第一个困惑点:

代码对数组进行求和并且不重置sum的原因是因为我们在preSum(之前的总和)中保存总和。然后,只要我们到达sum-k之前的总和(比如索引i),我们就知道之间的总和索引i和我们当前的索引正是k

例如,在下面带有i=2的图片中,我们当前的索引等于4,我们可以看到自9以来,我们当前索引的总和减去{{ 1}},索引3的总和为i,索引62(包括)之间的总和为4

Example Image

考虑这一点的另一种方法是看到从数组中丢弃6(在我们当前的[1,2]索引处)给出了一个总和4的子数组,原因类似于以上(详见图片)。

使用这种思维方法,我们可以说我们想要从数组的前面丢弃,直到我们留下一个sum 6的子数组。我们可以这样说,对于每个索引,“丢弃k,然后丢弃1,然后丢弃1+2等”(这些数字来自我们的示例),直到我们找到sum 1+2+3的子数组(在我们的示例中为k)。

这提供了一个非常有效的解决方案,但请注意我们将在数组的每个索引处执行此操作,从而反复对相同的数字求和。保存计算的一种方法是保存这些总和以供以后使用。更好的是,我们已经将这些相同的数字加在一起得到我们当前的k=6,所以我们可以保存这个数据。

要查找子阵列,我们只需查看已保存的总和,减去它们并测试我们剩下的是sum。必须减去每个已保存的总和有点烦人,所以我们可以使用commutativity of subtraction来查看如果k为真,sum-x=k也是如此。通过这种方式,我们可以看到sum-k=x是否是已保存的总和,如果是,则知道我们找到了大小为x的子数组。哈希映射使这种查找更有效。

现在为你的第二点困惑:

大多数时候你是对的,在找到合适的子阵列后我们可以做k。几乎总是,result++中的值将为preSum,因此1将等同于result+=preSum.get(sum-k)result+=1

唯一不合适的时间是result++之前已经达到的preSum.put。我们怎样才能回到我们已经拥有的sum?唯一的方法是使用负数来取消之前的数字,或者使用零,这根本不会影响总和。

基本上,当子阵列的总和等于0时,我们回到先前的sum。这类子阵列的两个例子是sum或普通的[2,-2]。使用这样的子阵列,我们需要将[0]添加到1 resultk+2-2=k,因此我们找到了多个子阵列,其中一个为零sum子阵列(k+0=k)和没有它的一个(sum=k+0)。

这也是sum=k+1的原因。每当我们再次达到相同的preSum.put时,我们就会找到另一个零和数组。使用两个零和子阵列,我们有三个带有sum的子阵列:原始数组(sum=k),原始数据加上第一个(sum=k)和原始数据(sum=k+0)({{ 1}})。这种逻辑也适用于更多数量的零和子阵列。