Java • Problem-Solving Patterns

Prefix Sums — Use prefix sums for range queries (intro)

Pre-compute cumulative totals so any range sum becomes a single subtraction.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 8 figures

SECTION 1

Historical Context & Motivation

Imagine you are building a weather app that stores the daily rainfall for an entire year. A user asks, "How much rain fell between March 15 and June 30?" The brute-force approach would loop through every day in that range and add up the values. That works, but what if thousands of users each ask about different date ranges every second? Looping each time quickly becomes too slow.

The idea of prefix sums — also called cumulative sums — has roots in early computing and mathematics. Mathematicians have long used cumulative totals in statistics and calculus, and computer scientists adopted the technique as soon as array-based computation became practical. The pattern is so fundamental that it appears in database engines, image processing, and competitive programming alike.

1940s • Cumulative Distribution Functions

Statisticians formalize cumulative sums to quickly compute probabilities over ranges of data, laying the mathematical groundwork for prefix sums.

1960s • Early Array Algorithms

As mainframe computers adopt array-based storage, engineers begin pre-computing running totals to speed up repeated range queries in scientific applications.

1984 • Summed-Area Table (SAT)

Franklin Crow publishes the summed-area table for texture mapping in computer graphics — a 2-D extension of prefix sums that computes rectangular region sums in constant time.

2000s • Competitive Programming Boom

Online judges like USACO and Codeforces popularize prefix sum problems, making the technique a must-know pattern for students learning algorithms.

The central question prefix sums answer is: How can we answer many range-sum queries on the same array without re-adding elements each time? By spending a small amount of work up front, we can reduce every future query to a single subtraction.

SECTION 2

Core Principles & Definitions

A prefix sum array is a helper array where each element stores the sum of all original elements from the start up to (and including) that index. Once built, you can find the sum of any contiguous sub-range in constant time — no loop required. The core ideas break down into a few simple principles.

1

Pre-computation

Build the prefix sum array in a single pass through the original data. This costs O(n) time and O(n) space, but it only happens once.
2

Range Query Formula

The sum from index L to R equals prefix[R] − prefix[L − 1]. This subtraction gives you the answer in O(1) time, no matter how large the range.
3

Trade-off: Space for Speed

You use extra memory (the prefix array) to avoid repeated work. This is a classic time–space trade-off that shows up throughout computer science.
4

Static Data Assumption

Basic prefix sums work best when the original array does not change between queries. If elements are updated frequently, more advanced structures are needed.
✦

KEY TAKEAWAY

Think of a prefix sum array like a running odometer on a car. To find the distance driven between two checkpoints, you don't re-drive the route — you simply subtract the earlier odometer reading from the later one. The prefix sum array records the "odometer" at every index, so any range total is just one subtraction.

SECTION 3

Visual Explanation

The diagram below shows how an original array is transformed into its prefix sum array, and then how a range query is answered by a single subtraction.

Building a Prefix Sum ArrayOriginal array arr[]i=02i=15i=21i=38i=43i=57cumulative addPrefix array pre[]i=02i=17i=28i=316i=419i=526Query: sum from i=1 to i=4Range L=1 to R=4Answer = pre[4] − pre[0] = 19 − 2 = 17Verify: 5 + 1 + 8 + 3 = 17 ✓
The top row is the original array. The purple row is the prefix sum array where each cell holds the cumulative total from index 0. The highlighted cyan region shows a range query answered by subtracting pre[0] from pre[4].

Notice how the prefix array at index i always equals the sum of arr[0] through arr[i]. To get the sum from index L to R, you take pre[R] − pre[L − 1]. If L is 0, you just use pre[R] directly since there is nothing to subtract. This single subtraction replaces what could be a loop over thousands of elements.

SECTION 4

Mathematical Framework

Let's formalize the two operations — building the prefix array and querying a range — with clear formulas and their Big-O complexities.

PREFIX ARRAY CONSTRUCTION

pre[i]=pre[i−1]+arr[i](fori≥1,withpre[0]=arr[0])pre[i] = pre[i − 1] + arr[i] (for i ≥ 1, with pre[0] = arr[0])

Where arr is the original array of length n, and pre is the prefix sum array. Building this takes O(n) time.

RANGE SUM QUERY

sum(L,R)=pre[R]−pre[L−1](ifL>0)sum(0,R)=pre[R]sum(L, R) = pre[R] − pre[L − 1] (if L > 0) sum(0, R) = pre[R]

L is the left index and R is the right index (both inclusive). Each query takes O(1) time — constant, regardless of range size.

TOTAL COMPLEXITY

O(n)pre−processing+O(1)perquery→O(n+Q)forQqueriesO(n) pre-processing + O(1) per query → O(n + Q) for Q queries

Compare this to brute force, which costs O(n × Q) in the worst case. For large n and Q, the prefix sum approach is dramatically faster.

⚠

Common Pitfall: Off-by-One Errors

Many students accidentally compute pre[R] − pre[L] instead of pre[R] − pre[L − 1]. This excludes arr[L] from the sum. A popular trick is to define pre with length n + 1, where pre[0] = 0 and pre[i] = pre[i − 1] + arr[i − 1]. Then the query becomes simply pre[R + 1] − pre[L], which is cleaner and avoids the special case when L = 0.

SECTION 5

Java Implementation & Walk-Through

Below is a complete Java implementation showing both the construction of the prefix sum array and range queries. We use the size n + 1 trick so that every query follows the same formula with no special cases.

Java: Prefix Sum — Build & Query// Build prefix sum array (size n+1, pre[0] = 0)int[] arr = {2, 5, 1, 8, 3, 7};int n = arr.length;int[] pre = new int[n + 1];// pre[0] stays 0 by defaultfor (int i = 0; i < n; i++) {pre[i + 1] = pre[i] + arr[i];}// Query: sum from index L to R (inclusive)static int rangeSum(int[] pre, int L, int R) {return pre[R + 1] - pre[L];}// Example callsrangeSum(pre, 1, 4); // → 17 (5+1+8+3)rangeSum(pre, 0, 5); // → 26 (entire array)rangeSum(pre, 3, 3); // → 8 (single element)pre[] = [0, 2, 7, 8, 16, 19, 26] — seven elements for a six-element array
The prefix array has length n + 1. Index 0 is always 0, so the range query formula pre[R+1] − pre[L] works uniformly, even when L is 0.

Notice the key detail: the prefix array pre has n + 1 elements. The extra slot at index 0 holds 0, which represents the sum of zero elements. This means pre[i+1] equals the sum of arr[0] through arr[i]. When you compute pre[R+1] − pre[L], the formula naturally handles every case including L = 0. You never need an if-statement to check for the boundary.

Index iarr[i]pre[i] (size n+1)Meaning of pre[i]
0—0Sum of 0 elements
12 (arr[0])2Sum of arr[0..0]
25 (arr[1])7Sum of arr[0..1]
31 (arr[2])8Sum of arr[0..2]
48 (arr[3])16Sum of arr[0..3]
53 (arr[4])19Sum of arr[0..4]
67 (arr[5])26Sum of arr[0..5]

Complete mapping between original array indices and the size n+1 prefix sum array

SECTION 6

Worked Example

A teacher records daily quiz scores for a student over one week: int[] scores = {88, 92, 75, 95, 83, 90, 78}. She wants to quickly answer questions like "What was the total score from Tuesday (index 1) to Friday (index 4)?" Let's solve this step by step using a prefix sum array.

Range Sum of Quiz Scores

  1. 1

    Step 1 — Identify the Data

    The original array is scores = {88, 92, 75, 95, 83, 90, 78} with n = 7. We need the sum from L = 1 to R = 4 (Tuesday through Friday).
  2. 2

    Step 2 — Build the Prefix Sum Array (size n + 1 = 8)

    Start with pre[0] = 0. Then: pre[1] = 0 + 88 = 88, pre[2] = 88 + 92 = 180, pre[3] = 180 + 75 = 255, pre[4] = 255 + 95 = 350, pre[5] = 350 + 83 = 433, pre[6] = 433 + 90 = 523, pre[7] = 523 + 78 = 601.
    pre = [0, 88, 180, 255, 350, 433, 523, 601]
  3. 3

    Step 3 — Apply the Range Query Formula

    sum(L, R) = pre[R + 1] − pre[L]. Substituting L = 1 and R = 4: sum(1, 4) = pre[5] − pre[1] = 433 − 88.
    sum(1, 4) = 345
  4. 4

    Step 4 — Verify by Direct Addition

    Adding scores[1] through scores[4] directly: 92 + 75 + 95 + 83 = 345. The answer matches, confirming the prefix sum approach is correct.
    ✓ Verified: 345
  5. 5

    Step 5 — Answer a Second Query for Free

    Now the teacher asks for the total from Wednesday (index 2) to Sunday (index 6). Using the same prefix array: sum(2, 6) = pre[7] − pre[2] = 601 − 180 = 421. No extra loops needed — we reused the prefix array.
    sum(2, 6) = 421

SECTION 7

Strengths, Limitations & Comparisons

Prefix sums are a powerful tool, but every tool has its limits. The table below compares the brute-force approach with prefix sums across several dimensions so you can see when each one is appropriate.

FeatureBrute-Force LoopPrefix Sum Array
Pre-processing timeNone (O(0))O(n) — one pass
Per-query timeO(n) worst caseO(1)
Total for Q queriesO(n × Q)O(n + Q)
Extra memoryNoneO(n)
Handles updates?Yes — always reads fresh dataNo — must rebuild after changes
Best when...Only 1 or 2 queries neededMany queries on static data

Brute-force vs. prefix sum comparison

💡

WHEN TO USE PREFIX SUMS

Use prefix sums whenever the problem asks for multiple range sum queries on data that doesn't change. If the data is frequently updated between queries, look ahead to structures like a Fenwick tree (Binary Indexed Tree) or a segment tree, which handle updates efficiently.

SECTION 8

Connection to Advanced Techniques

The prefix sum pattern is a stepping stone toward several more advanced data structures and algorithms. Understanding this foundation makes learning those topics far easier.

TechniqueWhat It AddsUse Case
2-D Prefix SumsExtends the idea to a grid (matrix). Each cell stores the sum of the rectangle from (0,0) to (r,c).Image processing, sub-rectangle sum queries
Difference ArrayThe inverse of prefix sums. Stores differences between consecutive elements; a prefix sum of the difference array recovers the original.Efficiently applying range updates (add k to every element from L to R)
Fenwick Tree (BIT)A tree structure that supports both point updates and prefix queries in O(log n) time.Dynamic data where both queries and updates happen frequently
Segment TreeA balanced binary tree that answers arbitrary range queries (min, max, sum, GCD, etc.) in O(log n) with updates.Competitive programming, database engines

Where prefix sums lead — related data structures

Each of these more advanced tools builds on the intuition you now have: pre-compute cumulative information so that queries become fast. Mastering the basic prefix sum array is your first step into this powerful family of techniques. If you go on to take AP Computer Science A or participate in competitive programming, you will see these ideas again and again.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

In your own words, explain why a prefix sum array lets you answer range sum queries in O(1) time. What information does each element of the prefix array encode?
Show answer
Each element pre[i] stores the total of all original array values from the beginning up through index i (or i − 1 in the n + 1 convention). Because pre[R + 1] already contains the sum from the start through index R, and pre[L] contains the sum from the start through index L − 1, subtracting one from the other cancels out all elements outside the desired range. This subtraction is a single arithmetic operation, which is O(1), regardless of whether the range contains 2 elements or 2 million.

Question 2: BASIC CALCULATION

Given arr = {4, 7, 2, 5, 9}, build the prefix sum array (using the n + 1 convention) and compute rangeSum(1, 3).
Show answer
First build the prefix array: pre[0] = 0, pre[1] = 0 + 4 = 4, pre[2] = 4 + 7 = 11, pre[3] = 11 + 2 = 13, pre[4] = 13 + 5 = 18, pre[5] = 18 + 9 = 27. So pre = [0, 4, 11, 13, 18, 27]. rangeSum(1, 3) = pre[3 + 1] − pre[1] = pre[4] − pre[1] = 18 − 4 = 14. You can verify: 7 + 2 + 5 = 14.

Question 3: INTERMEDIATE

You are given an integer array of length n and Q range-sum queries. If n = 100,000 and Q = 200,000, how many total operations does the brute-force approach require in the worst case? How many does the prefix sum approach require? Express each as a number.
Show answer
Brute force: in the worst case each query spans the whole array, so each costs n = 100,000 operations. Total = n × Q = 100,000 × 200,000 = 20,000,000,000 (20 billion) operations. Prefix sums: building the array costs n = 100,000 operations, and each query costs 1 operation, so total = n + Q = 100,000 + 200,000 = 300,000 operations. The prefix sum approach is roughly 66,667 times faster, which easily makes the difference between a program that finishes in milliseconds versus one that takes minutes.

Question 4: APPLIED

A fitness tracker records your daily step count for 30 days in an array steps[]. Write a Java method averageSteps(int[] steps, int L, int R) that uses prefix sums to return the average daily steps between day L and day R (inclusive). Assume the prefix array is already built and passed in. Return a double.
Show answer
The method should compute the range sum and divide by the number of days. Here is one correct implementation: static double averageSteps(int[] pre, int L, int R) { int total = pre[R + 1] - pre[L]; return (double) total / (R - L + 1); } The range sum is obtained using the standard prefix formula pre[R + 1] − pre[L]. The number of days in the range is R − L + 1. Casting to double before division ensures you get a decimal result rather than integer truncation. For example, if the total is 345 over 4 days, the method returns 86.25 rather than 86.

Question 5: CRITICAL THINKING

Suppose you have an array of 1,000,000 integers and you need to answer range sum queries. However, between every 10 queries, one element in the array gets updated. Should you use a basic prefix sum array, rebuild it after every update, or consider a different data structure? Justify your answer with Big-O reasoning.
Show answer
Rebuilding the prefix array after each update costs O(n) = O(1,000,000). If an update occurs every 10 queries, and you have many updates total, the rebuild cost dominates. For example, with 100,000 updates you would spend 100,000 × 1,000,000 = 10¹¹ operations just rebuilding. A Fenwick tree (Binary Indexed Tree) supports both point updates and prefix queries in O(log n) ≈ O(20) time. So the same 100,000 updates would cost only 100,000 × 20 = 2,000,000 operations. For this workload a Fenwick tree is the better choice. However, if updates were extremely rare (say, once per million queries), the simplicity and O(1) query time of a basic prefix sum array might still be preferable. The decision depends on the ratio of updates to queries.

SUMMARY

Lesson Summary

Summary

A prefix sum array stores cumulative totals so that any range sum query can be answered in O(1) time with a single subtraction. Building the array takes O(n) pre-processing and O(n) extra space. The core formula using the n + 1 convention is sum(L, R) = pre[R + 1] − pre[L], which avoids off-by-one errors and handles every boundary case cleanly.

This technique excels when the data is static (unchanging) and many queries are needed. For dynamic data, consider a Fenwick tree or segment tree. The prefix sum pattern also extends to two dimensions (2-D prefix sums) and has a natural inverse in the difference array technique. Master this foundational pattern, and you'll have a reliable tool for a wide variety of array problems.

Varsity Tutors • Java • Prefix Sums — Use prefix sums for range queries (intro)