Coding Interview Patterns

Course Content

Coding Interview Patterns

20 sections · 146 lessons

Contiguous Array


This problem hides a prefix sum behind a counting question. Nothing in the statement mentions sums, and the numbers are only 0 and 1. The move that unlocks it is a small re-labelling, and interviewers ask it precisely to see whether you can spot a known pattern in unfamiliar clothes.

It is also the cleanest example of the "longest" form of the pattern. Where Subarray Sum Equals K stored how many times each prefix appeared, this one stores where it first appeared — because the earliest start gives the longest subarray.

The balance returns to a value it has seen00101110123456after: -1after: -1 againafter: 0,seed at -1Count 0 as -1. Balance 0 was first seen before index 0, so indices 0 to 5 hold three of each.
Two positions with the same running balance bound a balanced subarray, and the earliest one gives the longest.

The problem

Given a list containing only 0s and 1s, return the length of the longest contiguous subarray that has the same number of 0s as 1s. Return 0 if there is none.

  • [0, 0, 1, 0, 1, 1, 1] → 6. Indices 0 to 5 hold 0, 0, 1, 0, 1, 1: three of each.
  • [1, 1, 1] → 0. There are no 0s at all.

Constraints: 1 ≤ n ≤ 10⁵, and every element is 0 or 1.

Clarifying questions

  • Return the length or the subarray? The length. (Returning the bounds is the same code, tracking two indices.)
  • Contiguous only? Yes. If any subset were allowed, the answer would just be 2 × min(zeros, ones).
  • What if no such subarray exists? Return 0.
  • Only 0 and 1? Yes. Other values would need a different mapping.

Approach 1: the simple way

Try every start, extend the end, and track a balance: +1 for each 1, −1 for each 0. When the balance is 0, the counts are equal.

Python
def find_max_length_brute(nums: list[int]) -> int:    """Try every start, extend the end, track zeros minus ones."""    best = 0    for start in range(len(nums)):        balance = 0        for end in range(start, len(nums)):            balance += 1 if nums[end] == 1 else -1            if balance == 0:                best = max(best, end - start + 1)    return best

Time is O(n²), space O(1). At n = 10⁵ that is about 5 × 10⁹ inner steps. Too slow, and the brute force already shows the way forward: it is computing a running balance, which is a prefix sum.

The key insight

Count every 0 as −1. Now "as many 0s as 1s" means "the −1s and +1s cancel", which means "the subarray sums to 0".

A subarray from i to j sums to 0 exactly when the running balance before i equals the running balance after j. In prefix terms, prefix[j + 1] - prefix[i] = 0, so prefix[i] = prefix[j + 1]. Two positions with the same balance bound a balanced subarray.

We want the longest one. For a given end j, the best start is the earliest position that had the same balance. So remember only the first index at which each balance appeared, and never overwrite it: a later index can only give a shorter subarray.

The seed follows from the same reasoning as in Subarray Sum Equals K. Before index 0 the balance is 0, and that empty prefix "happened" at position −1. So the map starts as {0: -1}, and a subarray starting at index 0 gets length j - (-1) = j + 1.

Walking along, the balance changes by exactly 1 per step. It is like tracking a lift that goes up one floor for each 1 and down one floor for each 0: every time the lift returns to a floor it has visited before, the trip in between had equal ups and downs.

Approach 2: first index of each balance

Python
def find_max_length(nums: list[int]) -> int:    """Longest subarray with as many 0s as 1s."""    first_seen = {0: -1}        # balance 0 happens before index 0    balance = 0    best = 0    for i, value in enumerate(nums):        balance += 1 if value == 1 else -1        if balance in first_seen:            best = max(best, i - first_seen[balance])        else:            first_seen[balance] = i     # keep only the earliest index    return best

Dry run on [0, 0, 1, 0, 1, 1, 1]

ivaluebalanceseen before?lengthbestfirst_seen after
start—0——0{0: −1}
00−1no—0{0: −1, −1: 0}
10−2no—0{0: −1, −1: 0, −2: 1}
21−1yes, at 02 − 0 = 22unchanged
30−2yes, at 13 − 1 = 22unchanged
41−1yes, at 04 − 0 = 44unchanged
510yes, at −15 − (−1) = 66unchanged
611no—6{…, 1: 6}

At i = 4, balance −1 was first seen at index 0, so indices 1 to 4 (0, 1, 0, 1) are balanced: length 4. At i = 5, balance 0 matches the seed at −1, so indices 0 to 5 are balanced: length 6. The final 1 pushes the balance to a new value, so it cannot extend anything.

Complexity. Time is O(n): one pass, O(1) average per map operation. Space is O(n) in the worst case — but the balance always lies between −n and n, so you can replace the map with a list of size 2n + 1, indexed by balance + n, if the interviewer asks for no hashing.

Edge cases

  • No balanced subarray. [1, 1, 1]: balances 1, 2, 3 never repeat, so the answer stays 0.
  • The whole array. [0, 1, 0, 0, 1, 1] → 6. The balance returns to 0 at the last index, and only the {0: -1} seed makes that count as length 6.
  • Length 1. [0] → 0. A single element can never be balanced.
  • Shortest possible answer. [0, 1] → 2.
  • Every answer is even. Equal counts means an even length. If your code ever returns an odd number, it is wrong — a free sanity check.

Saying it in the interview

Follow-ups

  • "Count the balanced subarrays instead." Store counts of each balance (seeded {0: 1}) and add the count on every repeat — exactly Subarray Sum Equals K with k = 0. On the lesson's example the count is 5.
  • "Longest subarray whose sum is exactly k" (any integers). Same first-index map; look up balance - k instead of balance.
  • "Longest subarray where 1s outnumber 0s." Now you need an earlier balance that is smaller, not equal. Because the balance moves by 1 per step, checking first_seen[balance - 1] is enough when the balance is not already positive — a neat trick worth deriving on the whiteboard.

Check your understanding

0 of 2 answered

1.For [1, 0, 1, 1, 0, 0], what does the function return?

2.Why does the map store the first index of each balance rather than the most recent one?