Files
leetcode/docs/(algorithms)/03-prefix-sum.mdx
T

120 lines
3.1 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 'Prefix Sum'
description: 'Precomputed cumulative state for O(1) range queries.'
---
## The idea
Precompute running totals once. Then any range sum costs O(1).
Like mile markers on a highway.
Distance from mile 30 to mile 80? Subtract: 80 30 = 50.
You do not re-drive the road.
**The formula:** `sum(i..j) = prefix[j + 1] - prefix[i]`
## The picture
```mermaid
flowchart TD
A["nums: 3 1 4 1 5"] --> B["prefix: 0 3 4 8 9 14"]
B --> C["sum(1..3) = prefix[4] prefix[1]<br/>= 9 3 = 6"]
C --> D["Check: 1 + 4 + 1 = 6 ✓"]
style D fill:#2e7d32,color:#fff
```
The leading 0 matters. It makes ranges that start at index 0 work with no special case.
## Range Sum Query (LC 303)
Pay O(n) once at build time. Answer every query in O(1).
```python
from itertools import accumulate
class NumArray:
def __init__(self, nums: list[int]):
self.prefix = [0] + list(accumulate(nums))
def sum_range(self, left: int, right: int) -> int:
return self.prefix[right + 1] - self.prefix[left]
```
## Prefix + Hashmap (LC 560 — Subarray Sum Equals K)
The signature trick of this topic. The question flips:
"Which subarrays sum to k?" becomes
"At each point, how many *earlier* prefixes equal `current k`?"
Because: if `prefix[j] prefix[i] = k`, the slice between them sums to k.
```mermaid
flowchart LR
A["Walk the array,<br/>carry running sum"] --> B["Ask the map:<br/>seen sum k before?"]
B --> C["Yes, m times →<br/>add m to answer"]
B --> D["Record current sum<br/>in the map"]
D --> A
style C fill:#2e7d32,color:#fff
```
```python
from collections import defaultdict
def subarray_sum(nums: list[int], k: int) -> int:
count = 0
current = 0
seen = defaultdict(int)
seen[0] = 1 # empty prefix — subarrays that start at 0
for n in nums:
current += n
count += seen[current - k] # ask first
seen[current] += 1 # record after
return count
```
**Order matters.** Ask before you record, or a subarray of length 0 counts itself when k = 0.
## Product variant (LC 238 — Product of Array Except Self)
Same idea, two directions. Prefix products from the left, suffix products from the right.
`answer[i] = left[i] × right[i]` — everything except i.
```python
def product_except_self(nums: list[int]) -> list[int]:
n = len(nums)
result = [1] * n
left = 1
for i in range(n):
result[i] = left
left *= nums[i]
right = 1
for i in range(n - 1, -1, -1):
result[i] *= right
right *= nums[i]
return result
```
## Complexity
| | Time | Space |
|---|---|---|
| Build | O(n) | O(n) |
| Each range query | O(1) | — |
| Prefix + hashmap | O(n) one pass | O(n) |
## Close-out ritual
Before you submit, say out loud:
1. Time and space complexity.
2. One edge case trace: range starting at 0, k = 0 with zeros in the array, or negative numbers (this is why sliding window fails and prefix + hashmap wins).
## Plan problems
**A-set:** LC 303 · 238 · 560
**B-set:** LC 525 · 974