What is the prefix sum technique, and when do you reach for it?
You precompute the running total of the first k elements for every k, then answer questions about any range by subtracting two of those totals. The sum of the elements from index left to index right is the prefix sum at right plus one minus the prefix sum at left.
def prefix_sums(nums):
sums = [0]
for value in nums:
sums.append(sums[-1] + value)
return sums
data = [2, 3, 5]
sums = prefix_sums(data)
print(sums)
print(sums[2] - sums[0])The outputs are the list 0, 2, 5, 10 and the number 5. The prefix list has one extra leading zero so that the prefix sum before the first element is well defined, which is what lets you compute the sum of a range starting at index zero without a special case.
The mechanism is that a range sum is a difference of two prefix sums. Precomputing once makes each query a constant-time subtraction instead of a scan over the range, so a program with many range queries over the same array goes from quadratic to linear overall.
The trade-off to name is that the prefix array costs O of n space, and it is only valid for a static array. If the values change, every prefix sum after that point is wrong and the whole array has to be rebuilt, which is linear per update. That is the moment to reach for a Fenwick tree or a segment tree instead: they support both queries and updates in logarithmic time at the cost of a more complex implementation and a larger constant factor. Knowing when the data is static and when it is not is the entire decision.