51. Contiguous Array
š LeetCode Problem: 525. Contiguous Array
š Difficulty: Medium
š·ļø Topics: Array, Hash Table, Prefix Sum
Problem Statement
Given a binary array nums, return the maximum length of a contiguous subarray with an equal number of 0 and 1.
Example 1:
Input: nums = [0,1]
Output: 2
Explanation: [0, 1] is the longest contiguous subarray with an equal number of 0 and 1.
Example 2:
Input: nums = [0,1,0]
Output: 2
Explanation: [0, 1] or [1, 0] are the longest contiguous subarrays with equal number of 0 and 1.
Constraints:
- 1 <= nums.length <= 10^5
- nums[i] is either 0 or 1.
šØ Visual Understanding
The Problem Visualized
nums = [0, 1, 0, 1, 1, 0]
Check subarrays:
[0, 1] ā 1 zero, 1 one ā length 2 ā
[0, 1, 0, 1] ā 2 zeros, 2 ones ā length 4 ā
[1, 0, 1, 1, 0] ā 2 zeros, 3 ones ā not equal ā
[0, 1, 0, 1, 1, 0] ā 3 zeros, 3 ones ā length 6 ā
Maximum length: 6
Key Observation: Transform to Balance Problem
šØ CRITICAL INSIGHT - Read This Carefully!
Instead of tracking 0s and 1s separately,
treat 0 as -1 and 1 as +1.
Equal number of 0s and 1s means sum = 0!
Original: [0, 1, 0, 1, 1, 0]
Transform: [-1, 1, -1, 1, 1, -1]
Find subarray with sum = 0:
[-1, 1] ā sum = 0 ā
[-1, 1, -1, 1] ā sum = 0 ā
[-1, 1, -1, 1, 1, -1] ā sum = 0 ā
šØ CRITICAL: Why Check for SAME Balance (Not Just Balance = 0)?
This is the key insight many people miss!
Question: "Why check if balance[i] == balance[j]?
Can't I just check if balance = 0?"
Answer: NO! Checking only balance = 0 will MISS many valid subarrays!
Case 1: Balanced subarray STARTING from index 0
nums = [0, 1, 0, 1]
Transform: [-1, +1, -1, +1]
Index: 0 1 2 3
Balance: -1 0 -1 0
ā ā
Zero! Zero!
Balance = 0 means:
Subarray from START (index 0) to current is balanced!
Index 1: [0,1] from 0-1 has 1 zero, 1 one ā
Index 3: [0,1,0,1] from 0-3 has 2 zeros, 2 ones ā
ā For subarrays starting at 0: YES, balance = 0 works!
Case 2: Balanced subarray NOT starting from index 0
nums = [1, 1, 1, 0, 0]
Transform: [+1, +1, +1, -1, -1]
Index: 0 1 2 3 4
Balance: +1 +2 +3 +2 +1
ā ā
Same balance = +2!
Balance NEVER equals 0!
If we ONLY check balance = 0:
Result: 0 (no balanced subarray found) ā WRONG!
But if we check for SAME balance:
balance = +2 at indices 1 and 3
Subarray from index 2 to 3: [1, 0]
Has 1 one and 1 zero ā
Length: 3 - 1 = 2
balance = +1 at indices 0 and 4
Subarray from index 1 to 4: [1, 1, 0, 0]
Has 2 ones and 2 zeros ā
Length: 4 - 0 = 4 ā
Maximum length: 4 ā
ā If we only checked balance = 0, we'd return 0 (completely wrong)!
The Mathematical Reason:
If balance[j] == balance[i]:
Then:
balance[j] = sum from index 0 to j
balance[i] = sum from index 0 to i
Subarray sum from i+1 to j:
= balance[j] - balance[i]
= balance[j] - balance[j] (same value!)
= 0 ā
Sum = 0 means equal 0s and 1s!
Visual Proof:
nums = [1, 0, 1, 1, 0, 0]
[+1, -1, +1, +1, -1, -1]
Index: 0 1 2 3 4 5
Balance: +1 0 +1 +2 +1 0
ā ā ā ā ā
ā ā āāāāāāāā ā
ā āāāāāāāāāāāāāā
Same! Same! Same!
Balance +1 appears at: 0, 2, 4
Indices 0 to 2: sum = balance[2] - balance[0] = +1 - (+1) = 0
Subarray [0, 1] from index 1-2 (1 zero, 1 one) ā
Indices 2 to 4: sum = balance[4] - balance[2] = +1 - (+1) = 0
Subarray [1, 0] from index 3-4 (1 one, 1 zero) ā
Indices 0 to 4: sum = balance[4] - balance[0] = +1 - (+1) = 0
Subarray [0, 1, 1, 0] from index 1-4 (2 zeros, 2 ones) ā
Balance 0 appears at: 1, 5
Indices 1 to 5: [1, 1, 0, 0] from index 2-5 (2 zeros, 2 ones) ā
Summary Table:
| What to Check | What It Finds | Can Miss? |
|---|---|---|
| balance = 0 ONLY | Subarrays starting from index 0 | ā YES - Misses middle subarrays! |
| balance[i] = balance[j] | ALL balanced subarrays | ā NO - Finds everything! |
Key Takeaway:
ā Check if CURRENT balance exists in HashMap
ā This handles BOTH:
1. Balance = 0 (subarray from start)
2. Balance = any repeated value (subarray anywhere)
Using HashMap with {0: -1} initialization handles ALL cases!
The Insight: Use Running Balance (Revised)
nums = [0, 1, 0, 1, 1, 0]
Transform to: [-1, 1, -1, 1, 1, -1]
Calculate running balance:
Index: -1 0 1 2 3 4 5
Value: -1 1 -1 1 1 -1
Balance: 0 -1 0 -1 0 1 0
ā ā ā ā
Same balance = 0!
Balance 0 appears at: -1, 1, 3, 5
Balance -1 appears at: 0, 2
When balance repeats:
The subarray between has sum = 0!
Equal number of 0s and 1s!
All valid subarrays:
Indices -1 to 1: [0,1] length 2
Indices 0 to 2: [1,0] length 2
Indices -1 to 3: [0,1,0,1] length 4
Indices 2 to 3: not applicable (same index check)
Indices -1 to 5: [0,1,0,1,1,0] length 6 ā Maximum!
Longest distance: 5 - (-1) = 6
Subarray: entire array [0,1,0,1,1,0]
Why This Works
If balance[j] == balance[i]:
Then:
balance[j] = sum from 0 to j
balance[i] = sum from 0 to i
sum(i+1 to j) = balance[j] - balance[i]
= balance[j] - balance[j] (same balance)
= 0
Sum = 0 means equal 0s and 1s! ā
šÆ Approach 1: Brute Force
The Most Natural Thinking š
Core Logic:
Check all subarrays:
1. For each starting position i
2. For each ending position j
3. Count 0s and 1s in subarray [i, j]
4. If equal, update max length
Implementation
public int findMaxLength(int[] nums) {
int maxLen = 0;
for (int i = 0; i < nums.length; i++) {
int zeros = 0, ones = 0;
for (int j = i; j < nums.length; j++) {
if (nums[j] == 0) {
zeros++;
} else {
ones++;
}
if (zeros == ones) {
maxLen = Math.max(maxLen, j - i + 1);
}
}
}
return maxLen;
}
ⰠTime: O(n²) - nested loops
š¾ Space: O(1) - only variables
šÆ Approach 2: Prefix Sum Array
Better Approach š”
Core Logic:
Transform problem:
1. Replace 0 with -1 (keep 1 as +1)
2. Build prefix sum array
3. Find pairs with same prefix sum
4. Distance between pairs = length of balanced subarray
Key insight:
If prefixSum[j] == prefixSum[i]
Then sum(i+1 to j) = 0
Which means equal 0s and 1s!
Visual Explanation:
nums = [0, 1, 0, 1, 1, 0]
Transform: [-1, 1, -1, 1, 1, -1]
Build prefix sum:
Index: -1 0 1 2 3 4 5
Prefix: 0 -1 0 -1 0 1 0
Find duplicate prefix sums:
Prefix sum 0 at: -1, 1, 3, 5
Prefix sum -1 at: 0, 2
Longest distance for prefix sum 0:
5 - (-1) = 6
Subarray: indices 0 to 5 (entire array)
Implementation
public int findMaxLength(int[] nums) {
int n = nums.length;
// Build prefix sum array (treat 0 as -1)
int[] prefixSum = new int[n + 1];
for (int i = 0; i < n; i++) {
int value = (nums[i] == 0) ? -1 : 1;
prefixSum[i + 1] = prefixSum[i] + value;
}
int maxLen = 0;
// Check all pairs with same prefix sum
for (int i = 0; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (prefixSum[i] == prefixSum[j]) {
maxLen = Math.max(maxLen, j - i);
}
}
}
return maxLen;
}
ⰠTime: O(n²) - checking all pairs
š¾ Space: O(n) - prefix sum array
ā Approach 3: HashMap with Running Balance (Optimal)
The Ultimate Optimization š”
Core Logic:
Use HashMap to track first occurrence of each balance:
Key insight:
Treat 0 as -1, 1 as +1
Track running balance (cumulative sum)
When same balance seen again:
- Subarray between has balance 0
- Equal number of 0s and 1s!
Algorithm:
1. Initialize map with {0: -1}
2. Track running balance
3. If balance seen before:
- Calculate length: current_index - first_index
- Update max length
4. If new balance: store it
Visual Explanation:
nums = [0, 1, 0, 1, 1, 0]
Running balance (0ā-1, 1ā+1):
Index: -1 0 1 2 3 4 5
Value: -1 +1 -1 +1 +1 -1
Balance: 0 -1 0 -1 0 +1 0
ā ā ā ā
Initial ā ā ā
Same Same Same
Map evolution:
map = {0: -1}
i=0: balance=-1, NEW ā map = {0: -1, -1: 0}
i=1: balance=0, SEEN at -1 ā length = 1-(-1) = 2
i=2: balance=-1, SEEN at 0 ā length = 2-0 = 2
i=3: balance=0, SEEN at -1 ā length = 3-(-1) = 4
i=4: balance=1, NEW ā map = {0: -1, -1: 0, 1: 4}
i=5: balance=0, SEEN at -1 ā length = 5-(-1) = 6 ā
Maximum length: 6
Why Store Index -1 for Balance 0?
Edge case: Balanced prefix from start
nums = [0, 1]
Transform: [-1, +1]
i=0: balance=-1, map = {0: -1, -1: 0}
i=1: balance=0, FOUND at -1!
length = 1 - (-1) = 2 ā
Without {0: -1}:
We'd miss this case!
Implementation
public int findMaxLength(int[] nums) {
// Map: balance -> first index where this balance was seen
Map<Integer, Integer> map = new HashMap<>();
// Initialize: balance 0 at index -1
// Handles case where prefix from start is balanced
map.put(0, -1);
int balance = 0;
int maxLen = 0;
for (int i = 0; i < nums.length; i++) {
// Treat 0 as -1, 1 as +1
balance += (nums[i] == 0) ? -1 : 1;
if (map.containsKey(balance)) {
// Found same balance before
int length = i - map.get(balance);
maxLen = Math.max(maxLen, length);
} else {
// First time seeing this balance
map.put(balance, i);
}
}
return maxLen;
}
ā° Time: O(n) - single pass
š¾ Space: O(n) - hashmap stores at most n+1 different balances
š Step-by-Step Execution
Approach 2 (Prefix Sum Array): nums = [1, 1, 1, 0, 0]
Initial State:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
nums = [1, 1, 1, 0, 0]
n = 5
STEP 1: Build Prefix Sum Array (Transform 0ā-1, 1ā+1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
prefixSum[0] = 0 (initial)
i=0: nums[0] = 1 ā value = +1
prefixSum[1] = 0 + 1 = 1
i=1: nums[1] = 1 ā value = +1
prefixSum[2] = 1 + 1 = 2
i=2: nums[2] = 1 ā value = +1
prefixSum[3] = 2 + 1 = 3
i=3: nums[3] = 0 ā value = -1
prefixSum[4] = 3 + (-1) = 2
i=4: nums[4] = 0 ā value = -1
prefixSum[5] = 2 + (-1) = 1
Final prefixSum array:
Index: 0 1 2 3 4 5
Prefix: 0 1 2 3 2 1
STEP 2: Find Pairs with Same Prefix Sum
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Check all pairs (i, j) where j > i:
i=0: prefixSum[0] = 0
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
j=1: prefixSum[1] = 1, 0 ā 1 ā
j=2: prefixSum[2] = 2, 0 ā 2 ā
j=3: prefixSum[3] = 3, 0 ā 3 ā
j=4: prefixSum[4] = 2, 0 ā 2 ā
j=5: prefixSum[5] = 1, 0 ā 1 ā
i=1: prefixSum[1] = 1
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
j=2: prefixSum[2] = 2, 1 ā 2 ā
j=3: prefixSum[3] = 3, 1 ā 3 ā
j=4: prefixSum[4] = 2, 1 ā 2 ā
j=5: prefixSum[5] = 1, 1 = 1 ā MATCH!
Length = 5 - 1 = 4
maxLen = max(0, 4) = 4
Subarray: indices 2 to 5 ā [1, 1, 0, 0]
Verification: 2 ones, 2 zeros ā
i=2: prefixSum[2] = 2
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
j=3: prefixSum[3] = 3, 2 ā 3 ā
j=4: prefixSum[4] = 2, 2 = 2 ā MATCH!
Length = 4 - 2 = 2
maxLen = max(4, 2) = 4
Subarray: indices 3 to 4 ā [1, 0]
Verification: 1 one, 1 zero ā
i=3: prefixSum[3] = 3
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
j=4: prefixSum[4] = 2, 3 ā 2 ā
j=5: prefixSum[5] = 1, 3 ā 1 ā
i=4: prefixSum[4] = 2
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
j=5: prefixSum[5] = 1, 2 ā 1 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
šÆ FINAL RESULT: maxLen = 4
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Best subarray: [1, 1, 0, 0] from indices 2-5
Has 2 ones and 2 zeros ā
Key Insight Demonstrated:
Balance NEVER equals 0 in this example!
If we only checked balance = 0, we'd get 0 (wrong!)
But by checking for SAME balance, we found length 4 ā
Approach 3 (HashMap): nums = [0, 1, 0, 1, 1, 0]
Initial State:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
nums = [0, 1, 0, 1, 1, 0]
n = 6
map = {0: -1}
balance = 0
maxLen = 0
Index 0: nums[0] = 0 (treat as -1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 0 + (-1) = -1
Check map for balance -1: NOT found
Add to map:
map = {0: -1, -1: 0}
maxLen = 0
Index 1: nums[1] = 1 (treat as +1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = -1 + 1 = 0
Check map for balance 0: FOUND at index -1!
Calculate length:
length = 1 - (-1) = 2
Update maxLen:
maxLen = max(0, 2) = 2
Don't update map (keep first occurrence):
map = {0: -1, -1: 0}
Subarray [0, 1]: 1 zero, 1 one ā
Index 2: nums[2] = 0 (treat as -1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 0 + (-1) = -1
Check map for balance -1: FOUND at index 0!
Calculate length:
length = 2 - 0 = 2
Update maxLen:
maxLen = max(2, 2) = 2
map = {0: -1, -1: 0}
Subarray from index 1 to 2: [1, 0] ā 1 zero, 1 one ā
Index 3: nums[3] = 1 (treat as +1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = -1 + 1 = 0
Check map for balance 0: FOUND at index -1!
Calculate length:
length = 3 - (-1) = 4
Update maxLen:
maxLen = max(2, 4) = 4
map = {0: -1, -1: 0}
Subarray from index 0 to 3: [0,1,0,1] ā 2 zeros, 2 ones ā
Index 4: nums[4] = 1 (treat as +1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 0 + 1 = 1
Check map for balance 1: NOT found
Add to map:
map = {0: -1, -1: 0, 1: 4}
maxLen = 4
Index 5: nums[5] = 0 (treat as -1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 1 + (-1) = 0
Check map for balance 0: FOUND at index -1!
Calculate length:
length = 5 - (-1) = 6
Update maxLen:
maxLen = max(4, 6) = 6
map = {0: -1, -1: 0, 1: 4}
Subarray from index 0 to 5: [0,1,0,1,1,0] ā 3 zeros, 3 ones ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
šÆ FINAL RESULT: 6
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
The entire array has equal 0s and 1s!
Example 2: nums = [0, 1, 0]
Initial State:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
nums = [0, 1, 0]
map = {0: -1}
balance = 0
maxLen = 0
Index 0: nums[0] = 0
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = -1
map = {0: -1, -1: 0}
Index 1: nums[1] = 1
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 0
Found at -1!
length = 1 - (-1) = 2
maxLen = 2
map = {0: -1, -1: 0}
Index 2: nums[2] = 0
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = -1
Found at 0!
length = 2 - 0 = 2
maxLen = max(2, 2) = 2
map = {0: -1, -1: 0}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
šÆ FINAL RESULT: 2
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Two valid subarrays of length 2:
[0, 1] at indices 0-1
[1, 0] at indices 1-2
Example 3 (CRITICAL): nums = [1, 1, 1, 0, 0]
This example demonstrates WHY we need to check for same balance, not just balance = 0!
Initial State:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
nums = [1, 1, 1, 0, 0]
map = {0: -1}
balance = 0
maxLen = 0
IMPORTANT: Watch how balance NEVER returns to 0!
Index 0: nums[0] = 1 (treat as +1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 0 + 1 = 1
Check map for balance 1: NOT found
Add to map:
map = {0: -1, 1: 0}
maxLen = 0
Index 1: nums[1] = 1 (treat as +1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 1 + 1 = 2
Check map for balance 2: NOT found
Add to map:
map = {0: -1, 1: 0, 2: 1}
maxLen = 0
Index 2: nums[2] = 1 (treat as +1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 2 + 1 = 3
Check map for balance 3: NOT found
Add to map:
map = {0: -1, 1: 0, 2: 1, 3: 2}
maxLen = 0
Index 3: nums[3] = 0 (treat as -1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 3 + (-1) = 2
Check map for balance 2: FOUND at index 1! ā
Calculate length:
length = 3 - 1 = 2
Update maxLen:
maxLen = max(0, 2) = 2
Don't update map (keep first occurrence):
map = {0: -1, 1: 0, 2: 1, 3: 2}
Subarray from index 2 to 3: [1, 0]
Verification: 1 one, 1 zero ā
š KEY INSIGHT: Balance = 2 (not 0), but SAME as before!
This means subarray sum = 0!
Index 4: nums[4] = 0 (treat as -1)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 2 + (-1) = 1
Check map for balance 1: FOUND at index 0! ā
Calculate length:
length = 4 - 0 = 4
Update maxLen:
maxLen = max(2, 4) = 4
map = {0: -1, 1: 0, 2: 1, 3: 2}
Subarray from index 1 to 4: [1, 1, 0, 0]
Verification: 2 ones, 2 zeros ā
š KEY INSIGHT: Balance = 1 (not 0), but SAME as at index 0!
This means subarray sum = 0!
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
šÆ FINAL RESULT: 4
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Best subarray: [1, 1, 0, 0] from indices 1-4
Has 2 ones and 2 zeros ā
šØ CRITICAL OBSERVATION:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Balance values seen: 1, 2, 3, 2, 1
Balance NEVER equals 0!
If we ONLY checked for balance = 0:
We would return maxLen = 0 ā COMPLETELY WRONG!
But by checking if balance exists in HashMap:
We found matching balances (2 at indices 1,3 and 1 at indices 0,4)
We correctly returned maxLen = 4 ā
This is WHY we check map.containsKey(balance) instead of just balance == 0!
Initial State:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
nums = [0, 1, 0]
map = {0: -1}
balance = 0
maxLen = 0
Index 0: nums[0] = 0
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = -1
map = {0: -1, -1: 0}
Index 1: nums[1] = 1
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = 0
Found at -1!
length = 1 - (-1) = 2
maxLen = 2
map = {0: -1, -1: 0}
Index 2: nums[2] = 0
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
balance = -1
Found at 0!
length = 2 - 0 = 2
maxLen = max(2, 2) = 2
map = {0: -1, -1: 0}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
šÆ FINAL RESULT: 2
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Two valid subarrays of length 2:
[0, 1] at indices 0-1
[1, 0] at indices 1-2
š Edge Cases
Case 1: All zeros
Input: nums = [0, 0, 0]
Output: 0
Explanation: No way to have equal 0s and 1s
Case 2: All ones
Input: nums = [1, 1, 1]
Output: 0
Explanation: No way to have equal 0s and 1s
Case 3: Single pair
Input: nums = [0, 1]
Output: 2
Explanation: Entire array is balanced
Case 4: Entire array balanced
Input: nums = [0, 1, 0, 1, 1, 0]
Output: 6
Case 5: No balanced subarray
Input: nums = [0, 0, 1]
Output: 2
Explanation: [0, 1] at indices 1-2
Case 6: Multiple balanced subarrays
Input: nums = [0, 1, 0, 1]
Output: 4
Explanation: Entire array
Case 7: Balanced at start
Input: nums = [0, 1, 1, 0, 1]
Output: 4
Explanation: [0, 1, 1, 0]
Case 8: Balanced at end
Input: nums = [1, 0, 1, 0, 1]
Output: 4
Explanation: [0, 1, 0, 1]
Case 9: Long array with small balance
Input: nums = [0, 0, 1, 0, 0, 0, 1, 1]
Output: 6
Explanation: [0, 1, 0, 0, 0, 1] has 4 zeros and 2 ones...
Actually [0, 0, 1, 0, 0, 0, 1, 1] at indices 1-8
has 4 zeros and 2 ones... Let me recalculate:
[0, 1, 0, 0, 0, 1] ā 4 zeros, 2 ones ā
[1, 0, 0, 0, 1, 1] ā 3 zeros, 3 ones ā (length 6)
Common Mistakes
Mistake 1: Not initializing map with {0: -1}
ā Wrong:
Map<Integer, Integer> map = new HashMap<>();
// Missing initialization
ā Right:
map.put(0, -1);
Reason: Handles balanced prefix from start
Mistake 2: Treating 0 and 1 directly instead of -1 and +1
ā Wrong:
balance += nums[i]; // Adding 0 or 1
ā Right:
balance += (nums[i] == 0) ? -1 : 1;
Reason: Need sum to be 0 for equal count
Mistake 3: Updating existing balance in map
ā Wrong:
map.put(balance, i); // Always update
ā Right:
if (!map.containsKey(balance)) {
map.put(balance, i);
}
Reason: Want earliest occurrence to maximize length
Mistake 4: Wrong length calculation
ā Wrong:
int length = i - map.get(balance) + 1;
// Off by one!
ā Right:
int length = i - map.get(balance);
Reason: Distance between indices, not including first
Mistake 5: Counting 0s and 1s separately
ā Wrong:
int zeros = 0, ones = 0;
// Track separately, compare at end
ā Right:
int balance = 0;
// Use balance, check when same
Reason: Balance approach is more efficient
Mistake 6: Not maximizing length
ā Wrong:
if (map.containsKey(balance)) {
return i - map.get(balance);
// Return immediately
}
ā Right:
if (map.containsKey(balance)) {
maxLen = Math.max(maxLen, i - map.get(balance));
// Keep checking for longer
}
Reason: Need to find MAXIMUM length, not first match
šÆ Key Takeaways
ā” Algorithm Comparison
Approach 1: Brute Force
Time: O(n²)
Space: O(1)
Use: Only for understanding
Count 0s and 1s for each subarray
Approach 2: Prefix Sum Array
Time: O(n²)
Space: O(n)
Use: Learning step
Build prefix sum, find pairs with same sum
Better understanding of the transformation
Approach 3: HashMap (RECOMMENDED)
Time: O(n)
Space: O(n)
Use: Optimal solution
Track balance with HashMap
Single pass, O(1) lookups
š The Core Insight
šØ MOST IMPORTANT CONCEPT - Don't Miss This!
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Problem Transformation:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Original problem:
Find longest subarray with equal 0s and 1s
Transform:
Treat 0 as -1, keep 1 as +1
Find longest subarray with sum = 0
Why this works:
Equal 0s and 1s means:
count(0) = count(1)
count(-1) + count(+1) = 0
sum = 0 ā
šØ CRITICAL: Why NOT Just Check Balance = 0?
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Many people think: "Just check if balance equals 0!"
This is WRONG and will fail many test cases!
Example that breaks "check balance = 0 only":
nums = [1, 1, 1, 0, 0]
Transform: [+1, +1, +1, -1, -1]
Balance: +1, +2, +3, +2, +1
Balance NEVER equals 0!
If checking only balance = 0: return 0 ā WRONG!
But [1, 1, 0, 0] is valid (2 ones, 2 zeros)!
Balance +1 at indices 0 and 4 ā length 4 ā
The Mathematical Truth:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
If balance[j] == balance[i]:
Then sum(i+1 to j) = balance[j] - balance[i] = 0 ā
This works for ANY balance value:
- balance = 0 (subarray from start)
- balance = 5 (subarray in middle)
- balance = -3 (subarray anywhere)
As long as balance REPEATS, subarray between is balanced!
Balance Concept:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Running balance = cumulative sum
When balance repeats:
balance[j] = balance[i]
Then:
sum(i+1 to j) = balance[j] - balance[i]
= same - same
= 0 ā
Which means equal 0s and 1s!
What to Check:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā WRONG: if (balance == 0)
Only finds subarrays starting from index 0
ā RIGHT: if (map.containsKey(balance))
Finds ALL balanced subarrays (anywhere)
HashMap with {0: -1} handles BOTH cases:
1. balance = 0 (prefix from start)
2. balance = any repeated value (subarray anywhere)
Algorithm Pattern:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Track running balance:
1. Initialize map with {0: -1}
2. For each element:
a. Update balance (+1 for 1, -1 for 0)
b. If balance exists in map:
- Calculate length: current - first
- Update max length
c. If new balance: store it
Why Map Balance ā Index?
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
We want to find when balance repeats:
- Store first occurrence
- When seen again, calculate distance
- Distance = length of balanced subarray
Space: O(n) because:
- At most n+1 different balances
- Range: from -n to +n
Why Index -1 for Balance 0?
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Handles prefix balanced from start:
nums = [0, 1]
i=0: balance=-1
i=1: balance=0 (back to initial!)
Found at -1
length = 1 - (-1) = 2 ā
Without {0: -1}, we'd miss this!
Time Complexity:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Single pass: O(n)
HashMap operations: O(1) average
Total: O(n)
Space Complexity:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
HashMap stores balances: O(n)
Range of possible balances: -n to +n (2n+1 values)
But we only store what we see: at most n+1
šÆ Pattern Recognition
Problem Type: Find Maximum Length with Constraint
Core Pattern: Running Balance + HashMap
When to Apply:
ā Binary array (0s and 1s)
ā Equal count constraint
ā Maximum length needed
ā Contiguous/continuous subarray
Recognition Keywords:
- "Equal number of"
- "Binary array"
- "Maximum length"
- "Contiguous"
Similar Problems:
- Continuous Subarray Sum (LC 523) - Problem #50
- Longest Well-Performing Interval (LC 1124)
- Maximum Size Subarray Sum Equals k (LC 325)
Key Difference from Problem #50:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā #50: Sum divisible by k ā
ā Track: sum % k ā
ā Goal: Check existence ā
ā ā
ā #51: Equal 0s and 1s ā
ā Track: balance (0ā-1, 1ā+1) ā
ā Goal: Find max length ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Both use HashMap, but different transformations!
š§ Interview Strategy
Step 1: "Find longest subarray with equal 0s and 1s"
Step 2: "Transform: treat 0 as -1, problem becomes sum=0"
Step 3: "Use running balance (cumulative sum)"
Step 4: "HashMap tracks first occurrence of each balance"
Step 5: "Same balance = subarray with sum 0"
Step 6: "Calculate length, track maximum"
Step 7: "O(n) time with single pass"
Key Points to Mention:
- Recognize transformation to sum problem
- Treat 0 as -1, 1 as +1
- Running balance concept
- HashMap stores balance ā first index
- Initialize {0: -1} for edge cases
- Store only first occurrence (maximize length)
- Time: O(n), Space: O(n)
- Single pass solution
Why Transformation?
"Instead of tracking two separate counts (0s and 1s),
I transform the problem by treating 0 as -1. This way,
equal counts means sum equals 0, which is much simpler
to track with a running balance and HashMap."
Why HashMap?
"The hashmap lets me instantly check if I've seen this
balance before in O(1), storing the earliest index to
maximize the distance when the balance repeats."
This is similar to problem #50! š
š Quick Revision Notes
šÆ Core Concept:
šØ CRITICAL: Check for SAME balance (not just balance=0)! Transform 0ā-1, find longest subarray with sum=0 by tracking repeating balance values with HashMap. Store first occurrence to maximize length.
Why Not Just Check Balance = 0?
nums = [1,1,1,0,0]
Balance: +1, +2, +3, +2, +1 (never 0!)
Only checking balance = 0: return 0 ā WRONG!
Checking repeating balance: return 4 ā CORRECT!
Key: balance[i] = balance[j] means sum(i+1 to j) = 0
ā” Quick Implementation (Optimal):
import java.util.HashMap;
class Test {
public int findMaxLength(int[] a) {
int res = Integer.MIN_VALUE;
int len = a.length;
// Approach 1 - using prefixsum array alone. O(N2)
// // Transform 0 to -1 and keep 1 same.
// for (int i = 0; i < len; i++) {
// if (a[i] == 0) {
// a[i] = -1;
// }
// }
// // Find prefix sum
// int[] prefixSum = new int[len + 1];
// for (int i = 1; i <= len; i++) {
// prefixSum[i] = prefixSum[i - 1] + a[i - 1];
// }
// // Find gap between indices where prefixsum value becomes equal to one
// another
// for (int i = 0; i <= len; i++) {
// for (int j = i + 1; j <= len; j++) {
// // whenever prefixsum becomes equal, calculate width among indices
// // to find the maximum length
// if (prefixSum[i] == prefixSum[j]) {
// res = Math.max(res, j - i);
// }
// }
// }
// Approach 2 - using prefixsum and hashmap
// Transform 0 to -1 and keep 1 same.
for (int i = 0; i < len; i++) {
if (a[i] == 0) {
a[i] = -1;
}
}
// Find prefix sum
int[] prefixSum = new int[len + 1];
for (int i = 1; i <= len; i++) {
prefixSum[i] = prefixSum[i - 1] + a[i - 1];
}
// Map to track last index (so that we get max len) of a prefixsum
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i <= len; i++) {
if (!map.containsKey(prefixSum[i])) {
map.put(prefixSum[i], i); // one-time. Keep it low index always to get max res
} else {
res = Math.max(res, i - map.get(prefixSum[i]));
}
}
return res == Integer.MIN_VALUE ? 0 : res;
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.findMaxLength(new int[] { 0, 1, 1, 1, 1, 1, 0, 0, 0 }));
System.out.println(t.findMaxLength(new int[] { 0, 1 }));
System.out.println(t.findMaxLength(new int[] { 0, 1, 0 }));
}
}
š Key Insights:
- šØ CRITICAL MISTAKE TO AVOID: Don't just check balance = 0!
- Pattern: Running balance + HashMap
- Transformation: 0 ā -1, 1 ā +1
- Goal: Find subarray with sum = 0
- Balance: Cumulative sum at each position
- Why same balance works: balance[j] = balance[i] means sum(i+1 to j) = 0
- Check: map.containsKey(balance) not balance == 0
- Example breaking "balance=0 only": [1,1,1,0,0] ā balance never 0 but answer is 4
- HashMap: Maps balance ā first index seen
- Initialize: {0: -1} handles balanced prefix
- Store once: Only first occurrence (maximize distance)
- Length calc: i - map.get(balance) (NOT +1)
- Update: Track maximum across all matches
- Time: O(n) - single pass
- Space: O(n) - at most n+1 different balances
šŖ Memory Aid:
"0 becomes -1! Sum = 0 means EQUAL! HashMap finds REPEATING balance!"
Think: "Balance tracker, same balance = answer!"
š” The Transformation:
Original: Equal 0s and 1s
[0, 1, 0, 1] ā 2 zeros, 2 ones ā
Transform: Sum = 0
[-1, +1, -1, +1] ā sum = 0 ā
Same problem, easier to solve!
š„ Why This Works:
If balance is same at two positions:
balance[j] = balance[i]
Then:
sum(i+1 to j) = balance[j] - balance[i]
= 0
Sum = 0 means:
-1s + 1s = 0
count(0s) = count(1s) ā
ā ļø Critical Details:
1. Initialize: map.put(0, -1)
2. Transform: (nums[i] == 0) ? -1 : 1
3. Store only first: if (!map.containsKey(balance))
4. Length: i - map.get(balance) (no +1)
5. Track maximum: Math.max(maxLen, length)
šÆ Comparison with Problem #50:
Problem #50 (Continuous Subarray Sum):
Track: sum % k (remainders)
Find: If divisible subarray exists
Return: boolean
Problem #51 (Contiguous Array):
Track: balance (0ā-1, 1ā+1)
Find: Maximum length with sum=0
Return: max length
Both use HashMap, different goals!
Related Patterns
- Continuous Subarray Sum (LC 523) - Problem #50
- Longest Well-Performing Interval (LC 1124)
- Maximum Size Subarray Sum Equals k (LC 325)
- Subarray Sum Equals K (LC 560)
Happy practicing! šÆ