S2Q1 · Largest Zero Sum Subarray¶
⚡ Quick Reference
Function: largest_zero_sum_subarray(nums: list) -> list
Core idea: use a prefix sum hash map - if the same prefix sum appears twice, the subarray between those two positions sums to zero.
def largest_zero_sum_subarray(nums: list) -> list:
prefix = {0: -1} # prefix_sum → first index where it occurred
total = 0
best_start, best_len = 0, 0
for i, x in enumerate(nums):
total += x
if total in prefix:
length = i - prefix[total]
if length > best_len:
best_len = length
best_start = prefix[total] + 1
else:
prefix[total] = i
return nums[best_start : best_start + best_len]
Key rules:
- prefix[sum] = index stores the first occurrence of each prefix sum
- If prefix[total] already exists, nums[prev+1 : i+1] sums to zero
- Keep the longest such subarray; ties → first occurrence wins
- Return [] if no zero-sum subarray found
Problem Statement¶
Problem
Write a function largest_zero_sum_subarray(nums) that returns the longest contiguous subarray with sum zero. If multiple subarrays share the maximum length, return the first one. Return [] if none exists.
Example:
[3, 4, -7, 1, 2, -3]
[3, 4, -7, 1, 2, -3]
Understanding the problem¶
A subarray is a contiguous slice. We need the longest one that sums to zero.
Brute force (O(n²)) - check every subarray:
This works but is slow for large inputs.
Prefix sum trick (O(n)) - the key insight:
If the prefix sum at index i equals the prefix sum at index j (where i < j), then the subarray from i+1 to j sums to zero.
prefix sums of [3, 4, -7, 1, 2, -3]:
index: -1 0 1 2 3 4 5
value: - 3 4 -7 1 2 -3
prefix sum: 0 3 7 0 1 3 0
↑ ↑
prefix[0] seen at index -1 and again at index 2
→ subarray [0..2] = [3, 4, -7] sums to 0
prefix[0] seen again at index 5
→ subarray [0..5] = [3, 4, -7, 1, 2, -3] sums to 0 ← longest!
Why initialise prefix = {0: -1}?
If the prefix sum reaches 0 at index i, it means nums[0..i] sums to zero. By storing 0 → -1 upfront, the length calculation i - prefix[0] = i - (-1) = i + 1 correctly gives the full subarray from index 0 to i.
Tracing the example¶
nums = [3, 4, -7, 1, 2, -3]
i |
x |
total |
In prefix? |
Action | best |
|---|---|---|---|---|---|
| - | - | 0 | - | Init prefix={0:-1} |
len=0 |
| 0 | 3 | 3 | No | prefix[3]=0 |
len=0 |
| 1 | 4 | 7 | No | prefix[7]=1 |
len=0 |
| 2 | -7 | 0 | ✅ at -1 | len=2-(-1)=3, start=0 | len=3 |
| 3 | 1 | 1 | No | prefix[1]=3 |
len=3 |
| 4 | 2 | 3 | ✅ at 0 | len=4-0=4, start=1 | len=4 |
| 5 | -3 | 0 | ✅ at -1 | len=5-(-1)=6, start=0 | len=6 |
Result: nums[0:6] = [3, 4, -7, 1, 2, -3]
Solution approaches¶
def largest_zero_sum_subarray(nums: list) -> list:
prefix = {0: -1}
total = 0
best_start, best_len = 0, 0
for i, x in enumerate(nums):
total += x
if total in prefix:
length = i - prefix[total]
if length > best_len:
best_len = length
best_start = prefix[total] + 1
else:
prefix[total] = i # only store FIRST occurrence
return nums[best_start : best_start + best_len]
def largest_zero_sum_subarray(nums: list) -> list:
n = len(nums)
best = []
for i in range(n):
total = 0
for j in range(i, n):
total += nums[j]
if total == 0 and j - i + 1 > len(best):
best = nums[i:j+1]
return best
Simple to understand - check every subarray, keep the longest zero-sum one. O(n²) time, fine for small inputs.
def largest_zero_sum_subarray(nums: list) -> list:
prefix = {0: -1}
total = 0
candidates = []
for i, x in enumerate(nums):
total += x
if total in prefix:
candidates.append((i - prefix[total], prefix[total] + 1))
else:
prefix[total] = i
if not candidates:
return []
best_len, best_start = max(candidates, key=lambda c: c[0])
return nums[best_start : best_start + best_len]
Collect all zero-sum subarray (length, start) pairs, then use max(key=lambda c: c[0]) to find the longest. The lambda selects by length; first occurrence wins because max returns the first maximum.
Key takeaways¶
Prefix sum trick - same sum twice means zero in between
If prefix_sum[i] == prefix_sum[j] (i < j), then nums[i+1..j] sums to zero. Storing the first occurrence in a dict lets you find this in O(1) per element.
Initialise prefix = {0: -1}
This handles the case where the prefix sum itself reaches zero - meaning the entire subarray from index 0 is zero-sum. Without this sentinel, you'd miss subarrays starting at index 0.
Only store the first occurrence
When a prefix sum is already in the map, don't overwrite it. You want the earliest occurrence to maximise the subarray length. The else branch ensures this.