天天看點

LeetCode560之和為K的子數組(相關話題:字首和)

題目描述

給定一個整數數組和一個整數 k,你需要找到該數組中和為 k 的連續的子數組的個數。

示例 1 :

輸入:nums = [1,1,1], k = 2

輸出: 2 , [1,1] 與 [1,1] 為兩種不同的情況。

說明 :

數組的長度為 [1, 20,000]。

數組中元素的範圍是 [-1000, 1000] ,且整數 k 的範圍是 [-1e7, 1e7]。

思路分析

LeetCode560之和為K的子數組(相關話題:字首和)

這個字首和數組preSum的含義也很好了解,preSum[i]就是nums[0..i-1]的和。那麼如果我們想求nums[i..j]的和,隻需要一步操作preSum[j+1]-preSum[i]即可,而不需要重新去周遊數組了。

int subarraySum(int[] nums, int k) {
    int n = nums.length;
    // 構造字首和
    int[] sum = new int[n + 1];
    sum[0] = 0; 
    for (int i = 0; i < n; i++)
        sum[i + 1] = sum[i] + nums[i];

    int ans = 0;
    // 窮舉所有子數組
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < i; j++)
            // sum of nums[j..i-1]
            if (sum[i] - sum[j] == k)
                ans++;

    return ans;
}      

這個解法的時間複雜度

LeetCode560之和為K的子數組(相關話題:字首和)

空間複雜度

LeetCode560之和為K的子數組(相關話題:字首和)

,并不是最優的解法。不過通過這個解法了解了字首和數組的工作原理之後,可以使用一些巧妙的辦法把時間複雜度進一步降低。

優化的思路是:我直接記錄下有幾個sum[j]和sum[i]-k相等,直接更新結果,就避免了内層的 for 循環。我們可以用哈希表,在記錄字首和的同時記錄該字首和出現的次數。

代碼實作

public int subarraySum(int[] nums, int k) {
        
        Map<Integer,Integer> map = new HashMap(); 
        int sum_i = 0;
        int sum_j = 0;
        int result = 0;
        map.put(0,1);
        for(int i=0;i<nums.length;i++){

            sum_i = sum_i + nums[i];
            //和目前字首和sum_i差k的字首和為sum_j
            sum_j = sum_i- k;
            if(map.get(sum_j)!=null){
                //之前存在這個字首和就把結果加1
                result = result + map.get(sum_j);
            }
             map.put(sum_i,map.getOrDefault(sum_i,0)+1);

        }

      return result;

  }      

繼續閱讀