feat(patterns): pointer/array tutorials

This commit is contained in:
2025-08-18 22:15:43 +01:00
parent a94e7f6142
commit 8ddd48508a
4 changed files with 1097 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
name: Prefix Sum
slug: prefix-sum
difficulty_level: 2
description: >
Precompute cumulative sums to answer range sum queries in O(1) time. This
transforms repeated O(n) range calculations into O(n) preprocessing plus
O(1) per query, making it essential for problems involving subarray sums.
when_to_use: |
- Range sum queries
- Subarray sum equals target
- Count subarrays with given sum
- Product of array except self
- 2D matrix region sums
metaphor: |
Imagine tracking your total running distance over a year. Instead of adding up
daily distances each time someone asks "how far did you run from day 50 to day
75?", you keep a running total. The cumulative distance on day 75 minus day 49
instantly gives you the answer.
Another analogy: a bank account balance. To find how much you spent between
two dates, you subtract the earlier balance from the later balance—no need to
sum individual transactions.
core_concept: |
A **prefix sum array** stores cumulative sums where `prefix[i]` = sum of all
elements from index 0 to i-1. This enables O(1) range sum queries:
**sum(i, j) = prefix[j+1] - prefix[i]**
The key insight is that any range sum can be computed from two prefix sums.
Instead of iterating through the range (O(n) per query), we do O(n) preprocessing
once and answer unlimited queries in O(1) each.
This pattern extends to:
- **Prefix products**: For multiplication-based problems
- **Prefix counts**: For counting occurrences
- **2D prefix sums**: For matrix region queries
- **Prefix sum + hash map**: For "subarray sum equals K"
visualization: |
**Building prefix sum array:**
```
Array: [3, 1, 4, 1, 5, 9]
Index: 0 1 2 3 4 5
prefix[0] = 0 (empty prefix)
prefix[1] = 0 + 3 = 3 (sum of first 1 element)
prefix[2] = 3 + 1 = 4 (sum of first 2 elements)
prefix[3] = 4 + 4 = 8
prefix[4] = 8 + 1 = 9
prefix[5] = 9 + 5 = 14
prefix[6] = 14 + 9 = 23
Prefix: [0, 3, 4, 8, 9, 14, 23]
Index: 0 1 2 3 4 5 6
```
**Range sum query: sum(2, 4) = elements at indices 2, 3, 4**
```
sum(2, 4) = prefix[5] - prefix[2]
= 14 - 4
= 10
Verification: arr[2] + arr[3] + arr[4] = 4 + 1 + 5 = 10 ✓
```
**Subarray sum equals K using hash map:**
```
Array: [1, 2, 3, -2, 5] K = 4
As we iterate, track prefix sums and look for prefix_sum - K:
i=0: prefix=1, need 1-4=-3, not found
i=1: prefix=3, need 3-4=-1, not found
i=2: prefix=6, need 6-4=2, not found
i=3: prefix=4, need 4-4=0, found! (empty prefix)
→ subarray [0:4] sums to 4
i=4: prefix=9, need 9-4=5, not found
Wait, also: prefix at i=1 is 3, at i=4 is 9-4=5... let me recalculate
Actually arr[1:3] = 2+3-2=3, arr[2:4]=3-2+5=6... checking for K=4:
subarray [0:4] → 1+2+3-2=4 ✓
```
code_template: |
def build_prefix_sum(arr: list[int]) -> list[int]:
"""Build prefix sum array. prefix[i] = sum of arr[0:i]."""
prefix = [0] * (len(arr) + 1)
for i in range(len(arr)):
prefix[i + 1] = prefix[i] + arr[i]
return prefix
def range_sum(prefix: list[int], i: int, j: int) -> int:
"""Sum of elements from index i to j (inclusive)."""
return prefix[j + 1] - prefix[i]
def subarray_sum_equals_k(nums: list[int], k: int) -> int:
"""Count subarrays with sum equal to k."""
count = 0
prefix_sum = 0
# Map: prefix_sum -> count of occurrences
sum_count = {0: 1} # Empty prefix has sum 0
for num in nums:
prefix_sum += num
# If (prefix_sum - k) exists, those prefixes form valid subarrays
if prefix_sum - k in sum_count:
count += sum_count[prefix_sum - k]
# Record current prefix sum
sum_count[prefix_sum] = sum_count.get(prefix_sum, 0) + 1
return count
def product_except_self(nums: list[int]) -> list[int]:
"""Product of array except self without division."""
n = len(nums)
result = [1] * n
# Prefix products (left to right)
prefix = 1
for i in range(n):
result[i] = prefix
prefix *= nums[i]
# Suffix products (right to left)
suffix = 1
for i in range(n - 1, -1, -1):
result[i] *= suffix
suffix *= nums[i]
return result
def matrix_region_sum(matrix: list[list[int]],
row1: int, col1: int,
row2: int, col2: int) -> int:
"""2D prefix sum for matrix region queries."""
# Build 2D prefix sum
m, n = len(matrix), len(matrix[0])
prefix = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
prefix[i][j] = (matrix[i-1][j-1]
+ prefix[i-1][j]
+ prefix[i][j-1]
- prefix[i-1][j-1])
# Query region sum using inclusion-exclusion
return (prefix[row2+1][col2+1]
- prefix[row1][col2+1]
- prefix[row2+1][col1]
+ prefix[row1][col1])
recognition_signals:
- "range sum"
- "subarray sum"
- "cumulative"
- "sum equals k"
- "count subarrays"
- "product except self"
- "matrix region sum"
- "running total"
- "continuous subarray"
common_mistakes:
- title: Off-by-one in prefix array indexing
description: |
Confusion about whether prefix[i] includes arr[i] or not leads to
incorrect range sums.
fix: |
Convention: `prefix[i]` = sum of first i elements = `arr[0:i]`.
So `prefix[0] = 0` (empty), and range sum is `prefix[j+1] - prefix[i]`.
- title: Forgetting the empty prefix for "sum equals K"
description: |
Not initializing the hash map with `{0: 1}` misses subarrays starting
from index 0.
fix: |
Always initialize: `sum_count = {0: 1}`. This handles the case where the
subarray from the beginning has sum K.
- title: Integer overflow with large sums
description: |
Prefix sums can grow very large when array elements are big, causing
overflow in some languages.
fix: |
In Python this isn't an issue. In Java/C++, use `long` for prefix sums
or check constraints carefully.
- title: Not handling negative numbers
description: |
Prefix sum works with negative numbers, but some may expect only positive
sums and use wrong optimization (like sliding window).
fix: |
Prefix sum handles negatives correctly. But problems like "minimum sum
subarray of size k" need different approaches when negatives are present.
variations:
- name: Basic prefix sum
description: |
Precompute cumulative sums for O(1) range queries. Foundation for other
variations.
example: "Range Sum Query - Immutable, Running Sum of 1d Array"
- name: Prefix sum with hash map
description: |
Track prefix sums in a hash map to find subarrays summing to a target.
Key insight: `prefix[j] - prefix[i] = target` means subarray [i,j] works.
example: "Subarray Sum Equals K, Contiguous Array"
- name: Prefix product
description: |
Same idea but with multiplication. Watch for zeros and consider using
left/right products separately.
example: "Product of Array Except Self"
- name: 2D prefix sum
description: |
Extend to matrices for O(1) region sum queries. Uses inclusion-exclusion
principle.
example: "Range Sum Query 2D, Matrix Block Sum"
- name: Difference array
description: |
Inverse of prefix sum. Store differences to support O(1) range updates.
Prefix sum of difference array gives original.
example: "Range Addition, Corporate Flight Bookings"
related_patterns:
- sliding-window
- two-pointers
prerequisite_patterns: []