Skip to content

Prefix Sum

  • What is prefix-sum?

    The prefix-sum technique is a way to efficiently calculate the cumulative sum of elements in an array or list. It involves creating a new array (or modifying the original in-place) where each element at index i represents the sum of all elements from the beginning of the original array up to and including index i.

  • Why is it important and what kind of problems does it solve?

    Prefix-sum is crucial for optimizing solutions to problems that require repeatedly calculating the sum of subarrays (or subsequences) of an array. Instead of recalculating the sum each time, which would take O(n) time, we can precompute the prefix-sum array and then find the sum of any subarray in O(1) time. This dramatically improves efficiency, especially when dealing with a large number of queries.

    It’s particularly useful for:

    • Range Sum Queries: Finding the sum of elements within a specific range (e.g., from index i to j).
    • Subarray Sum Problems: Identifying subarrays that satisfy certain conditions (e.g., finding the subarray with the maximum sum, or a subarray with a sum equal to a target value).
    • Image Processing: Calculating integral images for fast feature extraction.
    • Data Analysis: Computing cumulative statistics.
  • Core concepts, underlying principles, and key terminology.

    • Prefix Sum Array: The array that stores the cumulative sums. Let arr be the original array and prefix_sum be the prefix sum array. Then, prefix_sum[i] = arr[0] + arr[1] + ... + arr[i].
    • Range Sum Calculation: To find the sum of elements from index i to j (inclusive), where i <= j, we can use the formula: sum(i, j) = prefix_sum[j] - prefix_sum[i-1]. Note that if i is 0, then the sum is simply prefix_sum[j].
    • Base Case: The first element of the prefix sum array is equal to the first element of the original array: prefix_sum[0] = arr[0].
    • Cumulative Sum: The running total of the elements up to a certain point.

2. When to Use prefix-sum (and When Not To)

Section titled “2. When to Use prefix-sum (and When Not To)”
  • Identify problem patterns that suggest prefix-sum is a good fit.

    • Problems involving frequent range sum queries.
    • Problems asking to find subarrays that satisfy a specific sum condition.
    • Problems where you need to calculate cumulative sums or statistics.
    • The input array is relatively static; frequent modifications to the original array make prefix sums less efficient.
  • Discuss scenarios where a different data structure or algorithm would be more appropriate.

    • Frequent Updates to the Original Array: If the original array is frequently modified, a prefix-sum array would need to be recalculated each time, negating its performance benefits. In such cases, consider using a Segment Tree or Fenwick Tree (Binary Indexed Tree), which allow for efficient updates and range queries.
    • Searching for a Specific Element (not a sum): If the goal is simply to find a specific element in an array, other algorithms like binary search (if the array is sorted) or linear search would be more efficient.
    • Problems involving very large numbers: If the cumulative sum can exceed the maximum representable value of the data type, consider using appropriate data types or modular arithmetic to avoid overflow.
    • Problems with complex range queries: If the queries involve more complex operations than simple summation (e.g., finding the maximum or minimum in a range), a Segment Tree or other specialized data structure might be more appropriate.

3. Core Algorithm / Data Structure Template

Section titled “3. Core Algorithm / Data Structure Template”

Here’s a general template for approaching problems with prefix-sum:

  1. Create the Prefix Sum Array:

    • Initialize a new array prefix_sum of the same size as the input array arr.
    • Set prefix_sum[0] = arr[0].
    • Iterate through the rest of the array (from index 1 to n-1):
      • prefix_sum[i] = prefix_sum[i-1] + arr[i].
  2. Process Queries:

    • For each query (e.g., finding the sum of subarray from i to j):
      • If i == 0: sum = prefix_sum[j]
      • Else: sum = prefix_sum[j] - prefix_sum[i-1]
      • Use the calculated sum as needed.

4. Code Implementations (Python, Java, C++)

Section titled “4. Code Implementations (Python, Java, C++)”
def prefix_sum(arr):
"""
Calculates the prefix sum array of a given array.
Args:
arr: The input array (list of numbers).
Returns:
A list representing the prefix sum array.
"""
n = len(arr)
prefix_sum_arr = [0] * n
if n > 0:
prefix_sum_arr[0] = arr[0]
for i in range(1, n):
prefix_sum_arr[i] = prefix_sum_arr[i-1] + arr[i]
return prefix_sum_arr
def range_sum(prefix_sum_arr, i, j):
"""
Calculates the sum of elements in the original array from index i to j (inclusive)
using the prefix sum array.
Args:
prefix_sum_arr: The prefix sum array.
i: The starting index of the range.
j: The ending index of the range.
Returns:
The sum of elements from index i to j.
"""
if i == 0:
return prefix_sum_arr[j]
else:
return prefix_sum_arr[j] - prefix_sum_arr[i-1]
# Example usage:
arr = [1, 2, 3, 4, 5]
prefix_arr = prefix_sum(arr)
print(f"Original array: {arr}")
print(f"Prefix sum array: {prefix_arr}")
print(f"Sum from index 1 to 3: {range_sum(prefix_arr, 1, 3)}") # Output: 9 (2 + 3 + 4)
public class PrefixSum {
public static int[] prefixSum(int[] arr) {
int n = arr.length;
int[] prefixSumArr = new int[n];
if (n > 0) {
prefixSumArr[0] = arr[0];
for (int i = 1; i < n; i++) {
prefixSumArr[i] = prefixSumArr[i - 1] + arr[i];
}
}
return prefixSumArr;
}
public static int rangeSum(int[] prefixSumArr, int i, int j) {
if (i == 0) {
return prefixSumArr[j];
} else {
return prefixSumArr[j] - prefixSumArr[i - 1];
}
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int[] prefixArr = prefixSum(arr);
System.out.println("Original array: " + java.util.Arrays.toString(arr));
System.out.println("Prefix sum array: " + java.util.Arrays.toString(prefixArr));
System.out.println("Sum from index 1 to 3: " + rangeSum(prefixArr, 1, 3)); // Output: 9 (2 + 3 + 4)
}
}
#include <iostream>
#include <vector>
std::vector<int> prefixSum(const std::vector<int>& arr) {
int n = arr.size();
std::vector<int> prefixSumArr(n, 0);
if (n > 0) {
prefixSumArr[0] = arr[0];
for (int i = 1; i < n; i++) {
prefixSumArr[i] = prefixSumArr[i - 1] + arr[i];
}
}
return prefixSumArr;
}
int rangeSum(const std::vector<int>& prefixSumArr, int i, int j) {
if (i == 0) {
return prefixSumArr[j];
} else {
return prefixSumArr[j] - prefixSumArr[i - 1];
}
}
int main() {
std::vector<int> arr = {1, 2, 3, 4, 5};
std::vector<int> prefixArr = prefixSum(arr);
std::cout << "Original array: ";
for (int val : arr) {
std::cout << val << " ";
}
std::cout << std::endl;
std::cout << "Prefix sum array: ";
for (int val : prefixArr) {
std::cout << val << " ";
}
std::cout << std::endl;
std::cout << "Sum from index 1 to 3: " << rangeSum(prefixArr, 1, 3) << std::endl; // Output: 9 (2 + 3 + 4)
return 0;
}
## 5. Complexity Analysis
| Operation | Time Complexity | Space Complexity | Notes "
# prefix-sum: The Ultimate Cheatsheet
## 1. Detailed Explanation
- **What is prefix-sum?**
The prefix sum technique is a way to efficiently calculate the cumulative sum of elements in an array. Given an array `arr` of size `n`, the prefix sum array `prefix_sum` is an array of the same size such that `prefix_sum[i]` is the sum of all elements from `arr[0]` to `arr[i]`.
- **Why is it important and what kind of problems does it solve?**
Prefix sums are important because they allow us to compute the sum of any subarray in O(1) time after an initial O(n) preprocessing step. This is particularly useful when you need to answer multiple range sum queries on the same array.
It solves problems like:
* Range sum queries: Find the sum of elements within a given range [i, j].
* Subarray sum problems: Find if there exists a subarray with a given sum.
* Problems involving cumulative calculations.
- **Core concepts, underlying principles, and key terminology.**
* **Prefix Sum Array:** The array containing the cumulative sums.
* **Range Sum:** The sum of elements within a given range [i, j]. This can be calculated as `prefix_sum[j] - prefix_sum[i-1]` if `i > 0`, or `prefix_sum[j]` if `i == 0`.
* **Cumulative Sum:** The running total of elements up to a certain index.
## 2. When to Use prefix-sum (and When Not To)
- **Identify problem patterns that suggest prefix-sum is a good fit.**
* The problem involves frequent range sum queries.
* You need to find subarrays that satisfy a certain sum condition.
* The input array is relatively static (i.e., not frequently updated).
- **Discuss scenarios where a different data structure or algorithm would be more appropriate.**
* **Frequent updates to the array:** If the array is frequently updated, recalculating the prefix sum array after each update would be inefficient. Consider using Segment Trees or Binary Indexed Trees (Fenwick Trees), which support both updates and range queries efficiently (O(log n)).
* **Other types of range queries:** If the problem requires other types of range queries (e.g., finding the minimum or maximum element in a range), Segment Trees or Sparse Tables might be more suitable.
* **Simple problems:** If you only need to calculate the sum of a single range, calculating the sum directly might be simpler than constructing a prefix sum array.
## 3. Core Algorithm / Data Structure Template
1. **Build the Prefix Sum Array:**
* Create a new array `prefix_sum` of the same size as the input array `arr`.
* Initialize `prefix_sum[0] = arr[0]`.
* Iterate from `i = 1` to `n-1`: `prefix_sum[i] = prefix_sum[i-1] + arr[i]`.
2. **Answer Range Sum Queries:**
* Given a range `[i, j]` (inclusive):
* If `i == 0`: The sum is `prefix_sum[j]`.
* Otherwise: The sum is `prefix_sum[j] - prefix_sum[i-1]`.
## 4. Code Implementations (Python, Java, C++)
### Python
```python
def prefix_sum(arr):
"""
Calculates the prefix sum array of a given array.
Args:
arr: The input array (list of numbers).
Returns:
A list representing the prefix sum array.
"""
n = len(arr)
prefix_sum_arr = [0] * n
if n > 0:
prefix_sum_arr[0] = arr[0]
for i in range(1, n):
prefix_sum_arr[i] = prefix_sum_arr[i-1] + arr[i]
return prefix_sum_arr
def range_sum(prefix_sum_arr, i, j):
"""
Calculates the sum of elements in the original array from index i to j (inclusive)
using the prefix sum array.
Args:
prefix_sum_arr: The prefix sum array.
i: The starting index of the range.
j: The ending index of the range.
Returns:
The sum of elements from index i to j.
"""
if i == 0:
return prefix_sum_arr[j]
else:
return prefix_sum_arr[j] - prefix_sum_arr[i-1]
# Example usage:
arr = [1, 2, 3, 4, 5]
prefix_arr = prefix_sum(arr)
print(f"Original array: {arr}")
print(f"Prefix sum array: {prefix_arr}")
print(f"Sum from index 1 to 3: {range_sum(prefix_arr, 1, 3)}")
public class PrefixSum {
public static int[] prefixSum(int[] arr) {
int n = arr.length;
int[] prefixSumArr = new int[n];
if (n > 0) {
prefixSumArr[0] = arr[0];
for (int i = 1; i < n; i++) {
prefixSumArr[i] = prefixSumArr[i - 1] + arr[i];
}
}
return prefixSumArr;
}
public static int rangeSum(int[] prefixSumArr, int i, int j) {
if (i == 0) {
return prefixSumArr[j];
} else {
return prefixSumArr[j] - prefixSumArr[i - 1];
}
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int[] prefixArr = prefixSum(arr);
System.out.println("Original array: " + java.util.Arrays.toString(arr));
System.out.println("Prefix sum array: " + java.util.Arrays.toString(prefixArr));
System.out.println("Sum from index 1 to 3: " + rangeSum(prefixArr, 1, 3));
}
}
#include <iostream>
#include <vector>
std::vector<int> prefixSum(const std::vector<int>& arr) {
int n = arr.size();
std::vector<int> prefixSumArr(n, 0);
if (n > 0) {
prefixSumArr[0] = arr[0];
for (int i = 1; i < n; i++) {
prefixSumArr[i] = prefixSumArr[i - 1] + arr[i];
}
}
return prefixSumArr;
}
int rangeSum(const std::vector<int>& prefixSumArr, int i, int j) {
if (i == 0) {
return prefixSumArr[j];
} else {
return prefixSumArr[j] - prefixSumArr[i - 1];
}
}
int main() {
std::vector<int> arr = {1, 2, 3, 4, 5};
std::vector<int> prefixArr = prefixSum(arr);
std::cout << "Original array: ";
for (int val : arr) {
std::cout << val << " ";
}
std::cout << std::endl;
std::cout << "Prefix sum array: ";
for (int val : prefixArr) {
std::cout << val << " ";
}
std::cout << std::endl;
std::cout << "Sum from index 1 to 3: " << rangeSum(prefixArr, 1, 3) << std::endl;
return 0;
}
OperationTime ComplexitySpace Complexity
Building Prefix Sum ArrayO(n)O(n)
Range Sum QueryO(1)O(1)
  • Best Case: The best-case scenario is the same as the average and worst case for building the prefix sum array (O(n)). Range sum queries are always O(1).
  • Average Case: Same as best case.
  • Worst Case: Same as best case.
  • Tip: Consider using prefix sums for 2D arrays as well. You can calculate the prefix sum for each row or column, or a 2D prefix sum where prefix_sum[i][j] stores the sum of all elements in the rectangle from (0, 0) to (i, j).
  • Tip: For problems involving finding if a subarray exists with a certain sum, you can use prefix sums in conjunction with a hash map to improve efficiency. Store the prefix sums in the hash map, and for each index i, check if prefix_sum[i] - target exists in the map.
  • Pitfall: Off-by-one errors are common, especially when calculating range sums. Double-check your index calculations to ensure you’re including the correct elements. Remember that the range [i, j] is inclusive.
  • Pitfall: Integer overflow. When dealing with large numbers, the prefix sums can quickly become very large, potentially leading to integer overflow. Use appropriate data types (e.g., long in Java/C++, int in Python if the sums are small enough) or consider using modular arithmetic.
  • Pitfall: Modifying the Original Array: Be careful when modifying the original array in-place to store prefix sums, as it will overwrite the original values. If you need to preserve the original array, create a separate prefix sum array.

Description: Given an array of positive integers nums and a positive integer target, return the minimal length of a subarray whose sum is greater than or equal to target. If there is no such subarray, return 0 instead.

High-Level Approach:

  1. Calculate Prefix Sums: Create a prefix sum array prefix_sum for the input array nums.
  2. Iterate and Search: Iterate through the prefix_sum array. For each index j, we want to find the smallest index i such that prefix_sum[j] - prefix_sum[i-1] >= target (or prefix_sum[j] >= target if i==0).
  3. Binary Search (Optimization): For each j, we can use binary search to find the smallest i that satisfies the condition above. This optimizes the search from O(n) to O(log n) for each j.
  4. Track Minimum Length: Keep track of the minimum length found so far. If no subarray is found that meets the criteria, return 0.

Essentially, the prefix sum allows you to calculate any subarray sum in O(1) time, and you can then iterate through all possible subarrays to find the one that satisfies the condition. The binary search step is an optimization to avoid checking every possible starting index i for a given ending index j.