2Brute2Best
🔥0

Subarray with 0 Sum

Easy·GeeksforGeeksBasic Anchor
GeeksforGeeks
30-Second Mental ModelPrefix Sum + Hash Set (repeated prefix)
Complexity:O(N²)→O(N)
Key Insight

Keep a running total prefix. A block arr[l..r] sums to 0 exactly when the total before l equals the total after r. So walk once, store every total in a hash set, and return true the first time a total shows up again. Put 0 in the set at the start for blocks that begin at index 0.

Core Validity Invariant

A zero-sum subarray ends at index i exactly when the current prefix is already in seen — the same total appeared earlier, so everything added since then sums to 0.

01

Problem

You are given an array of integers arr. The numbers can be positive, negative or zero.

Is there at least one contiguous, non-empty subarray whose elements add up to exactly 0?

Write bool subArrayExists(vector<int>& arr) that returns true if such a subarray exists and false otherwise.

Input

An integer array arr of length n. Values may be negative.

Output

true if some non-empty contiguous subarray has sum 0, otherwise false.

Example 1
Input: arr = [4, 2, -3, 1, 6]
Output: true

The subarray [2, -3, 1] (indices 1..3) adds up to 0.

Example 2
Input: arr = [4, 2, 0, 1, 6]
Output: true

The single element [0] is a subarray with sum 0.

Example 3
Input: arr = [-3, 2, 3, 1, 6]
Output: false

No contiguous block of these numbers adds up to 0.

Constraints
  • • 1 ≤ n ≤ 10^4
  • • −10^5 ≤ arr[i] ≤ 10^5
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    How many subarrays does an array of length n have? Could I check them all?

  2. 2.

    If I keep a running total while walking from left to right, what does the total do across a block that sums to 0?

  3. 3.

    If the running total has the same value at two different moments, what do the numbers in between add up to?

  4. 4.

    Can a sliding window decide when to shrink if numbers can be negative?

  5. 5.

    What about a zero-sum block that starts at the very first element?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceFix each start, grow the end with a running sum, stop at sum 0O(N²)O(1)
OptimalA zero-sum subarray exists iff some running total repeats (seed the set with 0)O(N)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “contiguous subarray”A range [l..r] → its sum is a difference of two prefix sums.
  • “sum equals exactly 0”Exact target → look up a prefix value, not a window bound.
  • “numbers can be negative”A sliding window cannot decide when to shrink → use prefix sums.
  • “does one exist? (true/false)”We only need to know if a prefix was seen → a hash set is enough, no counts.
Contiguous range+sum exactly 0+negatives allowed+yes/no answer→Prefix Sum + Hash Set (repeated prefix)

This is the anchor of the Exact sum + hash map sub-pattern. The key move: "some range sums to K" becomes "some earlier prefix equals prefix − K". Here K = 0, so we look for a prefix equal to the current one, and since we only need yes/no, a set is enough. The next question (count zero-sum subarrays) keeps the same idea but needs a map of counts, and Subarray Sum Equals K then changes the lookup key to prefix − k.

10

Don't make this mistake

Using a sliding window
✗ sum += arr[r]; while (sum > 0 && l <= r) sum -= arr[l++]; if (sum == 0 && l <= r) return true;
✓ if (seen.count(prefix)) return true; seen.insert(prefix);

This is a pattern-confusion trap. A window drops the left element when the sum is "too big", which assumes the sum can only grow as the window grows. With negatives that is false. On [2, -1, -1] the window drops the 2 right away (sum 2 > 0), then never sees 2 − 1 − 1 = 0, and returns false.

Forgetting to put 0 in the set first
✗ unordered_set<long long> seen; // empty
✓ unordered_set<long long> seen; seen.insert(0);

A zero-sum block that starts at index 0 means the running total itself becomes 0. Without the starting 0 in the set there is nothing to match. [-3, 3, 5] has totals -3, 0, 5 — all different — so the code wrongly returns false.

Inserting before checking
✗ seen.insert(prefix); if (seen.count(prefix)) return true;
✓ if (seen.count(prefix)) return true; seen.insert(prefix);

The check is always true after inserting, so the code returns true on the first element. We must compare with earlier totals only.

Thinking you need the subarray's exact position

The question only asks yes/no, so a set is enough. If you later need the subarray itself (or the longest one), store prefix → first index in a map instead.

11

Want to implement this?

Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Prefix Sum translate into production architectures and low-level memory mechanics.