cover

前綴和把「區間求和」從 O(n) 降到 O(1)。差分陣列把「區間更新」從 O(n) 降到 O(1)。兩個互為逆運算,一起學最有效率。

先講結論

前綴和差分陣列
預處理O(n)O(n)
區間查詢O(1)需還原 O(n)
區間更新O(n)O(1)
適用多次查詢多次更新

多次查詢用前綴和,多次更新用差分。搞反了就是把 O(1) 用成 O(n)。

前綴和

原始陣列:  [1, 2, 3, 4, 5]
前綴和:    [0, 1, 3, 6, 10, 15]
              ↑ 哨兵(很重要!)

區間和 [1,3] = prefix[4] - prefix[1] = 10 - 1 = 9  → (2+3+4)
long[] prefix = new long[n + 1];
for (int i = 0; i < n; i++)
    prefix[i + 1] = prefix[i] + nums[i];
 
// 查詢 [left, right]
long sum = prefix[right + 1] - prefix[left];

那個 prefix[0] = 0 的哨兵很關鍵。沒有它,查詢 [0, right] 就需要特殊處理。加了哨兵,公式統一。

二維前綴和

矩陣:            前綴和:
1  2  3          0  0  0  0
4  5  6          0  1  3  6
7  8  9          0  5  12 21
                 0  12 27 45

查詢子矩陣 (r1,c1) 到 (r2,c2):

目標 = 全部 - 上面 - 左邊 + 左上角(被減兩次所以加回來)
sum = prefix[r2+1][c2+1] - prefix[r1][c2+1] - prefix[r2+1][c1] + prefix[r1][c1]

容斥原理。面試手畫一下圖就很清楚,不用死背公式。

差分陣列

前綴和的逆運算。用於「O(1) 區間加值」:

要對 [1,3] 加 2:
diff[1] += 2
diff[4] -= 2  (如果 4 < n)

然後做前綴和還原出結果
diff[left] += val;
if (right + 1 < n) diff[right + 1] -= val;
 
// 還原
for (int i = 1; i < n; i++)
    result[i] = result[i-1] + diff[i];

實際場景:航班預訂(LeetCode 1109)、拼車(LeetCode 1094)——大量的區間更新操作,一個一個改太慢,差分陣列一次 O(1) 搞定。

前綴和 + HashMap:最常考的進階技巧

「和為 K 的子陣列個數」——暴力 O(n²),前綴和 + HashMap O(n)。

核心洞見:如果 prefix[j] - prefix[i] == k,那 [i, j) 的子陣列和就是 k。所以只要看之前有沒有出現過 prefix[j] - k

int subarraySum(int[] nums, int k) {
    Map<Long, Integer> map = new HashMap<>();
    map.put(0L, 1);  // 前綴和為 0 出現 1 次
    long sum = 0;
    int count = 0;
 
    for (int num : nums) {
        sum += num;
        count += map.getOrDefault(sum - k, 0);
        map.merge(sum, 1, Integer::sum);
    }
    return count;
}

這個「前綴和 + HashMap」的組合在面試裡出現的頻率超高。Two Sum 的 HashMap 技巧你懂了,這題就只是把它套在前綴和上。


前綴和就像你的銀行帳戶餘額:你不需要把每筆交易重新加一遍,看一眼餘額就知道總共多少。差分陣列則是你的交易明細——記錄「變化量」而不是「絕對值」。