1664. Ways to Make a Fair Array
Problem Description
You are given an integer array nums
. You need to find how many indices you can choose to remove such that after removing exactly one element at that index, the resulting array becomes "fair".
An array is considered fair when the sum of all elements at even indices (0, 2, 4, ...) equals the sum of all elements at odd indices (1, 3, 5, ...).
The key insight is that when you remove an element at index i
, all elements after position i
shift left by one position, which means:
- Elements that were at even indices after
i
will now be at odd indices - Elements that were at odd indices after
i
will now be at even indices
For example, given nums = [6,1,7,4,1]
:
- Original even indices sum:
6 + 7 + 1 = 14
(indices 0, 2, 4) - Original odd indices sum:
1 + 4 = 5
(indices 1, 3) - If we remove index 1 (value 1), we get
[6,7,4,1]
:- New even indices sum:
6 + 4 = 10
(indices 0, 2) - New odd indices sum:
7 + 1 = 8
(indices 1, 3) - This is not fair since
10 ≠ 8
- New even indices sum:
The solution tracks:
s1
: Total sum of elements at even indices in the original arrays2
: Total sum of elements at odd indices in the original arrayt1
: Running sum of elements at even indices up to current positiont2
: Running sum of elements at odd indices up to current position
For each position i
, it calculates what the even and odd sums would be after removing element at index i
, accounting for the index shifts that occur after removal. The answer counts how many positions result in equal even and odd sums after removal.
Intuition
The critical observation is understanding what happens when we remove an element at index i
: all elements after position i
experience an index shift. Elements that were at even positions become odd-positioned, and vice versa.
Let's think about how to calculate the sums after removing an element at index i
:
- Elements before index
i
: These keep their original positions (even stays even, odd stays odd) - Element at index
i
: This gets removed - Elements after index
i
: These all shift left by one position, flipping their parity (even becomes odd, odd becomes even)
If we denote:
t1
= sum of even-indexed elements from start up to (but not including) indexi
t2
= sum of odd-indexed elements from start up to (but not including) indexi
s1
= total sum of all even-indexed elements in original arrays2
= total sum of all odd-indexed elements in original array
Then after removing element at index i
:
-
If
i
is even:- New even sum =
t1
(beforei
) +s2 - t2
(odd elements afteri
that become even) - New odd sum =
t2
(beforei
) +s1 - t1 - nums[i]
(even elements afteri
that become odd, excludingnums[i]
)
- New even sum =
-
If
i
is odd:- New even sum =
t1
(beforei
) +s2 - t2 - nums[i]
(odd elements afteri
that become even, excludingnums[i]
) - New odd sum =
t2
(beforei
) +s1 - t1
(even elements afteri
that become odd)
- New even sum =
This approach allows us to check each position in a single pass through the array. We maintain running sums (t1
, t2
) as we traverse, and for each position, we can calculate in O(1)
time whether removing that element would create a fair array. This gives us an efficient O(n)
solution instead of the naive O(n²)
approach of actually removing each element and recalculating sums.
Learn more about Prefix Sum patterns.
Solution Approach
The implementation follows a single-pass algorithm with prefix sum tracking:
Step 1: Calculate Initial Sums
s1, s2 = sum(nums[::2]), sum(nums[1::2])
s1
stores the total sum of elements at even indices (0, 2, 4, ...)s2
stores the total sum of elements at odd indices (1, 3, 5, ...)- Using Python slicing
nums[::2]
gets every other element starting from index 0, andnums[1::2]
gets every other element starting from index 1
Step 2: Initialize Tracking Variables
ans = t1 = t2 = 0
ans
counts the number of valid removal positionst1
tracks the running sum of even-indexed elements seen so fart2
tracks the running sum of odd-indexed elements seen so far
Step 3: Iterate Through Each Position
for i, v in enumerate(nums):
For each element v
at position i
, we check if removing it would create a fair array.
Step 4: Check Fairness After Removal
For even index (i % 2 == 0
):
ans += i % 2 == 0 and t2 + s1 - t1 - v == t1 + s2 - t2
- Left side
t2 + s1 - t1 - v
: New odd sum after removalt2
: odd elements before positioni
s1 - t1 - v
: even elements after positioni
(excluding current elementv
) that become odd
- Right side
t1 + s2 - t2
: New even sum after removalt1
: even elements before positioni
s2 - t2
: odd elements after positioni
that become even
For odd index (i % 2 == 1
):
ans += i % 2 == 1 and t2 + s1 - t1 == t1 + s2 - t2 - v
- Left side
t2 + s1 - t1
: New odd sum after removalt2
: odd elements before positioni
s1 - t1
: even elements after positioni
that become odd
- Right side
t1 + s2 - t2 - v
: New even sum after removalt1
: even elements before positioni
s2 - t2 - v
: odd elements after positioni
(excluding current elementv
) that become even
Step 5: Update Running Sums
t1 += v if i % 2 == 0 else 0 t2 += v if i % 2 == 1 else 0
After checking the current position, we update our running sums:
- If
i
is even, addv
tot1
- If
i
is odd, addv
tot2
The boolean expressions in the ans +=
lines evaluate to 1 (True) when both conditions are met, incrementing the answer counter. This elegant approach avoids explicit if-statements while maintaining clarity.
Time Complexity: O(n)
- single pass through the array
Space Complexity: O(1)
- only using a constant amount of extra variables
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [2, 1, 6, 4]
:
Initial Setup:
- Calculate total sums:
s1
(even indices sum) =nums[0] + nums[2]
=2 + 6 = 8
s2
(odd indices sum) =nums[1] + nums[3]
=1 + 4 = 5
- Initialize:
ans = 0
,t1 = 0
,t2 = 0
Iteration 1 (i=0, v=2):
- Check if removing index 0 creates a fair array
- Since i=0 is even:
- New odd sum =
t2 + (s1 - t1 - v)
=0 + (8 - 0 - 2)
=6
- New even sum =
t1 + (s2 - t2)
=0 + (5 - 0)
=5
- Is
6 == 5
? No, so don't incrementans
- New odd sum =
- Update:
t1 = 0 + 2 = 2
(add to even sum since i is even)
Iteration 2 (i=1, v=1):
- Check if removing index 1 creates a fair array
- Since i=1 is odd:
- New odd sum =
t2 + (s1 - t1)
=0 + (8 - 2)
=6
- New even sum =
t1 + (s2 - t2 - v)
=2 + (5 - 0 - 1)
=6
- Is
6 == 6
? Yes! Incrementans = 1
- New odd sum =
- Update:
t2 = 0 + 1 = 1
(add to odd sum since i is odd)
Iteration 3 (i=2, v=6):
- Check if removing index 2 creates a fair array
- Since i=2 is even:
- New odd sum =
t2 + (s1 - t1 - v)
=1 + (8 - 2 - 6)
=1
- New even sum =
t1 + (s2 - t2)
=2 + (5 - 1)
=6
- Is
1 == 6
? No, so don't incrementans
- New odd sum =
- Update:
t1 = 2 + 6 = 8
(add to even sum since i is even)
Iteration 4 (i=3, v=4):
- Check if removing index 3 creates a fair array
- Since i=3 is odd:
- New odd sum =
t2 + (s1 - t1)
=1 + (8 - 8)
=1
- New even sum =
t1 + (s2 - t2 - v)
=8 + (5 - 1 - 4)
=8
- Is
1 == 8
? No, so don't incrementans
- New odd sum =
- Update:
t2 = 1 + 4 = 5
(add to odd sum since i is odd)
Result: ans = 1
We found that removing the element at index 1 creates a fair array [2, 6, 4]
where:
- Even indices sum:
2 + 4 = 6
(indices 0, 2) - Odd indices sum:
6
(index 1) - Both sums equal 6, confirming the array is fair!
Solution Implementation
1class Solution:
2 def waysToMakeFair(self, nums: List[int]) -> int:
3 # Calculate initial sums of elements at even and odd indices
4 even_sum = sum(nums[::2]) # Sum of elements at even indices (0, 2, 4, ...)
5 odd_sum = sum(nums[1::2]) # Sum of elements at odd indices (1, 3, 5, ...)
6
7 # Initialize counters
8 fair_count = 0 # Count of indices that make the array fair when removed
9 left_even_sum = 0 # Sum of even-indexed elements to the left of current position
10 left_odd_sum = 0 # Sum of odd-indexed elements to the left of current position
11
12 # Iterate through each index to check if removing it makes the array fair
13 for index, value in enumerate(nums):
14 # When we remove an element at index i:
15 # - Elements before i keep their parity (even/odd position)
16 # - Elements after i switch their parity (even becomes odd, odd becomes even)
17
18 if index % 2 == 0: # Current element is at an even index
19 # After removal:
20 # new_even_sum = left_even_sum + (odd_sum - left_odd_sum)
21 # new_odd_sum = left_odd_sum + (even_sum - left_even_sum - value)
22 if left_odd_sum + even_sum - left_even_sum - value == left_even_sum + odd_sum - left_odd_sum:
23 fair_count += 1
24 left_even_sum += value
25 else: # Current element is at an odd index
26 # After removal:
27 # new_even_sum = left_even_sum + (odd_sum - left_odd_sum - value)
28 # new_odd_sum = left_odd_sum + (even_sum - left_even_sum)
29 if left_odd_sum + even_sum - left_even_sum == left_even_sum + odd_sum - left_odd_sum - value:
30 fair_count += 1
31 left_odd_sum += value
32
33 return fair_count
34
1class Solution {
2 public int waysToMakeFair(int[] nums) {
3 // Calculate initial sums of elements at even and odd indices
4 int totalEvenSum = 0;
5 int totalOddSum = 0;
6 int n = nums.length;
7
8 // Compute total sums for even-indexed and odd-indexed elements
9 for (int i = 0; i < n; i++) {
10 if (i % 2 == 0) {
11 totalEvenSum += nums[i];
12 } else {
13 totalOddSum += nums[i];
14 }
15 }
16
17 // Track prefix sums as we iterate
18 int prefixEvenSum = 0;
19 int prefixOddSum = 0;
20 int fairWaysCount = 0;
21
22 // Check each position to see if removing that element makes the array fair
23 for (int i = 0; i < n; i++) {
24 int currentValue = nums[i];
25
26 // When we remove element at index i:
27 // - Elements before i keep their parity (even/odd index)
28 // - Elements after i switch their parity (even becomes odd, odd becomes even)
29
30 if (i % 2 == 0) {
31 // Removing an even-indexed element
32 // New even sum = prefix even sum + (total odd sum - prefix odd sum)
33 // New odd sum = prefix odd sum + (total even sum - prefix even sum - current value)
34 int newEvenSum = prefixEvenSum + (totalOddSum - prefixOddSum);
35 int newOddSum = prefixOddSum + (totalEvenSum - prefixEvenSum - currentValue);
36
37 if (newEvenSum == newOddSum) {
38 fairWaysCount++;
39 }
40
41 prefixEvenSum += currentValue;
42 } else {
43 // Removing an odd-indexed element
44 // New even sum = prefix even sum + (total odd sum - prefix odd sum - current value)
45 // New odd sum = prefix odd sum + (total even sum - prefix even sum)
46 int newEvenSum = prefixEvenSum + (totalOddSum - prefixOddSum - currentValue);
47 int newOddSum = prefixOddSum + (totalEvenSum - prefixEvenSum);
48
49 if (newEvenSum == newOddSum) {
50 fairWaysCount++;
51 }
52
53 prefixOddSum += currentValue;
54 }
55 }
56
57 return fairWaysCount;
58 }
59}
60
1class Solution {
2public:
3 int waysToMakeFair(vector<int>& nums) {
4 // Calculate total sum of elements at even and odd indices
5 int totalEvenSum = 0, totalOddSum = 0;
6 int n = nums.size();
7
8 for (int i = 0; i < n; ++i) {
9 if (i % 2 == 0) {
10 totalEvenSum += nums[i];
11 } else {
12 totalOddSum += nums[i];
13 }
14 }
15
16 // Track prefix sums as we iterate
17 int prefixEvenSum = 0, prefixOddSum = 0;
18 int fairIndicesCount = 0;
19
20 for (int i = 0; i < n; ++i) {
21 int currentValue = nums[i];
22
23 // After removing element at index i:
24 // - Elements before i keep their parity
25 // - Elements after i switch parity (even becomes odd, odd becomes even)
26
27 if (i % 2 == 0) {
28 // Removing an even-indexed element
29 // New even sum = prefix even + (total odd - prefix odd)
30 // New odd sum = prefix odd + (total even - prefix even - current)
31 int newEvenSum = prefixEvenSum + (totalOddSum - prefixOddSum);
32 int newOddSum = prefixOddSum + (totalEvenSum - prefixEvenSum - currentValue);
33
34 if (newEvenSum == newOddSum) {
35 fairIndicesCount++;
36 }
37
38 prefixEvenSum += currentValue;
39 } else {
40 // Removing an odd-indexed element
41 // New even sum = prefix even + (total odd - prefix odd - current)
42 // New odd sum = prefix odd + (total even - prefix even)
43 int newEvenSum = prefixEvenSum + (totalOddSum - prefixOddSum - currentValue);
44 int newOddSum = prefixOddSum + (totalEvenSum - prefixEvenSum);
45
46 if (newEvenSum == newOddSum) {
47 fairIndicesCount++;
48 }
49
50 prefixOddSum += currentValue;
51 }
52 }
53
54 return fairIndicesCount;
55 }
56};
57
1/**
2 * Counts the number of indices where removing the element makes the array "fair"
3 * An array is fair when the sum of odd-indexed elements equals the sum of even-indexed elements
4 * @param nums - The input array of numbers
5 * @returns The count of indices that make the array fair when removed
6 */
7function waysToMakeFair(nums: number[]): number {
8 // s1: total sum of elements at even indices
9 // s2: total sum of elements at odd indices
10 // t1: running sum of elements at even indices (before current position)
11 // t2: running sum of elements at odd indices (before current position)
12 let evenSum: number = 0;
13 let oddSum: number = 0;
14 let leftEvenSum: number = 0;
15 let leftOddSum: number = 0;
16
17 const length: number = nums.length;
18
19 // Calculate total sums for even and odd indices
20 for (let i = 0; i < length; ++i) {
21 if (i % 2 === 0) {
22 evenSum += nums[i];
23 } else {
24 oddSum += nums[i];
25 }
26 }
27
28 let fairCount: number = 0;
29
30 // Check each index for fairness after removal
31 for (let i = 0; i < length; ++i) {
32 const currentValue: number = nums[i];
33
34 // When removing element at even index:
35 // New even sum = leftEvenSum + (oddSum - leftOddSum)
36 // New odd sum = leftOddSum + (evenSum - leftEvenSum - currentValue)
37 if (i % 2 === 0) {
38 if (leftOddSum + evenSum - leftEvenSum - currentValue === leftEvenSum + oddSum - leftOddSum) {
39 fairCount++;
40 }
41 }
42
43 // When removing element at odd index:
44 // New even sum = leftEvenSum + (oddSum - leftOddSum - currentValue)
45 // New odd sum = leftOddSum + (evenSum - leftEvenSum)
46 if (i % 2 === 1) {
47 if (leftOddSum + evenSum - leftEvenSum === leftEvenSum + oddSum - leftOddSum - currentValue) {
48 fairCount++;
49 }
50 }
51
52 // Update running sums based on current index parity
53 if (i % 2 === 0) {
54 leftEvenSum += currentValue;
55 } else {
56 leftOddSum += currentValue;
57 }
58 }
59
60 return fairCount;
61}
62
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. This is because:
- Computing
s1 = sum(nums[::2])
takesO(n)
time as it iterates through approximatelyn/2
elements - Computing
s2 = sum(nums[1::2])
takesO(n)
time as it iterates through approximatelyn/2
elements - The main
for
loop iterates through alln
elements exactly once, performing constant time operations in each iteration - Overall:
O(n) + O(n) + O(n) = O(n)
The space complexity is O(1)
. The algorithm only uses a fixed number of variables:
s1
ands2
store the initial sums of even and odd indexed elementsans
,t1
, andt2
are counters/accumulatorsi
andv
are loop variables- No additional data structures are created that scale with input size
All variables use constant space regardless of the input array size, resulting in O(1)
space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Handling Index Shifts After Removal
The most common mistake is failing to recognize that when an element is removed, all elements after it shift left by one position, causing their index parity to flip.
Incorrect Approach:
# WRONG: Simply removing element and checking sums without accounting for shifts
def waysToMakeFair_wrong(nums):
count = 0
for i in range(len(nums)):
temp = nums[:i] + nums[i+1:] # Remove element at index i
even_sum = sum(temp[::2])
odd_sum = sum(temp[1::2])
if even_sum == odd_sum:
count += 1
return count
Why it's wrong: After removing an element at index i
, the indices of all subsequent elements decrease by 1. Elements that were at even indices become odd-indexed, and vice versa. The naive approach above recalculates indices in the new array but doesn't account for this shift pattern correctly.
Correct Solution: Track prefix sums and understand that:
- Elements before the removed index keep their parity
- Elements after the removed index switch their parity
2. Off-by-One Errors in Prefix Sum Calculation
Another common mistake is updating the prefix sums before checking the fairness condition, leading to incorrect calculations.
Incorrect Order:
# WRONG: Updating prefix sums before checking
for i, v in enumerate(nums):
if i % 2 == 0:
t1 += v # Updated too early!
if t2 + s1 - t1 == t1 + s2 - t2: # Now t1 includes current element
ans += 1
else:
t2 += v # Updated too early!
if t2 + s1 - t1 == t1 + s2 - t2: # Now t2 includes current element
ans += 1
Correct Order: Always check the fairness condition first, then update the prefix sums:
for i, v in enumerate(nums):
# Check fairness BEFORE updating prefix sums
if i % 2 == 0:
if t2 + s1 - t1 - v == t1 + s2 - t2:
ans += 1
t1 += v # Update AFTER checking
else:
if t2 + s1 - t1 == t1 + s2 - t2 - v:
ans += 1
t2 += v # Update AFTER checking
3. Confusing Even/Odd Index Logic
It's easy to mix up the formulas for even and odd indices when calculating the new sums after removal.
Remember the pattern:
- When removing an even-indexed element: subtract its value from the even sum calculation
- When removing an odd-indexed element: subtract its value from the odd sum calculation
- The element being removed doesn't participate in either sum after removal
Visual Aid for Index i
removal:
Original: [a₀, a₁, a₂, a₃, a₄, a₅, ...] even odd even odd even odd Remove a₂: [a₀, a₁, a₃, a₄, a₅, ...] even odd even odd even (indices after removal point shift) Elements a₃, a₄, a₅... switch their parity!
What's the output of running the following function using the following tree as input?
1def serialize(root):
2 res = []
3 def dfs(root):
4 if not root:
5 res.append('x')
6 return
7 res.append(root.val)
8 dfs(root.left)
9 dfs(root.right)
10 dfs(root)
11 return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4 StringJoiner res = new StringJoiner(" ");
5 serializeDFS(root, res);
6 return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10 if (root == null) {
11 result.add("x");
12 return;
13 }
14 result.add(Integer.toString(root.val));
15 serializeDFS(root.left, result);
16 serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2 let res = [];
3 serialize_dfs(root, res);
4 return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8 if (!root) {
9 res.push("x");
10 return;
11 }
12 res.push(root.val);
13 serialize_dfs(root.left, res);
14 serialize_dfs(root.right, res);
15}
16
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!