3854. Minimum Operations to Make Array Parity Alternating
Problem Description
You are given an integer array nums.
An array is called parity alternating if for every index i where 0 <= i < n - 1, the values nums[i] and nums[i + 1] have different parity — meaning one is even and the other is odd.
In one operation, you may pick any index i and either increase nums[i] by 1 or decrease nums[i] by 1.
Your task is to return an integer array answer of length 2, where:
answer[0]is the minimum number of operations required to make the array parity alternating.answer[1]is the minimum possible value ofmax(nums) - min(nums), taken over all parity-alternating arrays that can be obtained by performing exactlyanswer[0]operations.
A few important points to keep in mind:
- A parity-alternating array can take one of two shapes: even numbers at even indices and odd numbers at odd indices, or odd numbers at even indices and even numbers at odd indices.
- Each operation changes a single element by exactly
1, which also flips its parity. So fixing the parity of any single element costs exactly1operation. - After achieving the minimum number of operations, among all valid resulting arrays, you want the one whose spread (the gap between the largest and smallest values) is as small as possible.
- An array of length
1is always considered parity alternating, so it requires0operations and has a spread of0.
How We Pick the Algorithm
Why Greedy Algorithms?
This problem maps to Greedy Algorithms through a short path in the full flowchart.
Greedy evaluation of two parity shapes minimizes operations and spread.
Open in FlowchartIntuition
The first thing to notice is that a parity-alternating array has only two possible shapes:
- Even numbers sit at even indices, and odd numbers sit at odd indices.
- Odd numbers sit at even indices, and even numbers sit at odd indices.
There is no other arrangement that can satisfy the alternating condition, because once we fix the parity at index 0, the parity at every other index is forced.
This immediately suggests that we should try both shapes, compute the cost for each, and then pick the better one.
For a given shape, how do we count the operations? At each index i, we know the expected parity of the element there. If the current nums[i] already matches the expected parity, it costs nothing. If it does not match, we need to flip its parity. Since each +1 or -1 operation flips parity, flipping the parity of one element costs exactly 1 operation. So the operation count is simply the number of positions where the actual parity differs from the expected parity. This gives us answer[0] for each shape.
A neat way to check the expected parity at index i is the expression (x - i) & 1. As i moves from one index to the next, the parity of i alternates, so subtracting i from x aligns all the "matching" positions to the same target value k, where k is the parity we want at even indices.
Now for the second part, the spread. Once the number of operations is fixed, we still have freedom in how we adjust each mismatched element. Whenever we change an element by 1, we can go either up or down, and both choices keep the operation count the same. To keep max(nums) - min(nums) small, we should make smart choices:
- If the element being changed is the current minimum of the array, decreasing it would push the minimum even lower and widen the spread, so we increase it by
1instead. - If the element is the current maximum, increasing it would widen the spread, so we decrease it by
1instead. - For any element strictly between the min and max, nudging it by
1in either direction stays within the existing range, so it never affects the overall spread.
By tracking the running minimum and maximum of the adjusted values, we can directly compute the resulting spread. One subtle detail: because adjacent elements must always differ in parity, the smallest possible spread for an array of length 2 or more can never be 0 — neighboring values must differ, so the spread is at least 1. That is why we take max(1, b - a).
Finally, we compare the two shapes by [operations, spread]. We prefer fewer operations; if the operation counts tie, we prefer the smaller spread. Comparing the two result lists directly captures this priority order automatically.
Pattern Learn more about Greedy patterns.
Solution Approach
We use a greedy strategy combined with a helper function that evaluates each of the two possible parity-alternating shapes.
Step 1: Handle the trivial case.
If the array has length 1, it is already parity alternating, so we return [0, 0] immediately — no operations and a spread of 0.
Step 2: Precompute the global minimum and maximum.
Before checking the shapes, we compute mn = min(nums) and mx = max(nums). These tell us which elements are the current extremes, which is exactly the information we need to make smart adjustment choices later.
Step 3: Define the helper function f(k).
The parameter k represents the desired parity at even indices: k = 0 means even numbers go at even indices, and k = 1 means odd numbers go at even indices. The function returns a list [cnt, spread].
Inside f(k), we initialize:
cnt = 0, the number of operations.a = infandb = -inf, the running minimum and maximum of the resulting array.
We then iterate over each element with its index using enumerate. For each (i, x):
- We check the expected parity using
(x - i) & 1. Subtractingialigns every "correctly placed" position to the same target valuek, since the parity ofiitself alternates as we move across the array. - If
(x - i) & 1 != k, the element is in the wrong parity slot, so we add1tocnt. Then we decide the direction of the adjustment to keep the spread small:- If
x == mn, we increase it:x += 1(avoid pushing the minimum lower). - Else if
x == mx, we decrease it:x -= 1(avoid pushing the maximum higher). - Otherwise, the element is strictly inside the range, so we leave the in-loop value as is — either direction stays within the existing extremes and does not affect the spread.
- If
- After possibly adjusting
x, we update the running extremes:a = min(a, x)andb = max(b, x).
After the loop, we return [cnt, max(1, b - a)]. The max(1, b - a) guard ensures the spread is at least 1, because in any array of length 2 or more, adjacent elements have different parity and therefore can never be equal.
Step 4: Compare the two shapes.
We call f(0) and f(1) and return min(f(0), f(1)). Comparing two lists in Python compares element by element, so this automatically picks the shape with the fewer operations first; if the operation counts are equal, it then picks the one with the smaller spread — exactly matching the problem's priority.
Complexity:
- Time complexity is
O(n), wherenis the length ofnums. ComputingmnandmxisO(n), and each call tofscans the array once. - Space complexity is
O(1), since we only use a constant number of variables beyond the input.
Example Walkthrough
Let's trace through the solution with a small example: nums = [3, 7, 5, 4].
Step 1: Check the trivial case.
The array has length 4, not 1, so we proceed with the full algorithm.
Step 2: Precompute global min and max.
mn = min([3, 7, 5, 4]) = 3mx = max([3, 7, 5, 4]) = 7
These tell us that 3 is the current minimum and 7 is the current maximum, which guides our adjustment choices later.
Step 3: Evaluate the first shape with f(0).
Here k = 0, meaning even numbers should sit at even indices and odd numbers at odd indices. We check each element using (x - i) & 1 and compare it against k = 0.
i | x | (x - i) & 1 | Matches k=0? | Action | Adjusted x |
|---|---|---|---|---|---|
| 0 | 3 | (3-0)&1 = 1 | No (need even) | mismatch; x == mn (3) → increase | 4 |
| 1 | 7 | (7-1)&1 = 0 | No (need odd) | mismatch; not min, not max → leave | 7 |
| 2 | 5 | (5-2)&1 = 1 | No (need even) | mismatch; not min, not max → leave | 5 |
| 3 | 4 | (4-3)&1 = 1 | No (need odd) | mismatch; not min, not max → leave | 4 |
Wait — all four positions mismatch, so cnt = 4. Let me reconsider this shape's intent: at index 0 we want even, index 1 odd, index 2 even, index 3 odd. The original [3, 7, 5, 4] is odd, odd, odd, even — all four are wrong, confirming cnt = 4.
Tracking running extremes on adjusted values [4, 7, 5, 4]:
a = min(4, 7, 5, 4) = 4b = max(4, 7, 5, 4) = 7
Spread = max(1, 7 - 4) = 3.
So f(0) = [4, 3].
Step 4: Evaluate the second shape with f(1).
Here k = 1, meaning odd numbers should sit at even indices and even numbers at odd indices.
i | x | (x - i) & 1 | Matches k=1? | Action | Adjusted x |
|---|---|---|---|---|---|
| 0 | 3 | (3-0)&1 = 1 | Yes (odd) | no change | 3 |
| 1 | 7 | (7-1)&1 = 0 | Yes (even slot, 7 is odd → match) | no change | 7 |
| 2 | 5 | (5-2)&1 = 1 | Yes | no change | 5 |
| 3 | 4 | (4-3)&1 = 1 | Yes | no change | 4 |
Every position already matches the expected parity, so cnt = 0. The array [3, 7, 5, 4] is already parity alternating (odd, odd? — let's verify parity: 3 odd, 7 odd...).
Let me double-check: 3 and 7 are both odd, which violates alternation! So the table check needs care. The expression (x - i) & 1 for index 1 is (7-1)&1 = 6&1 = 0, and we compare against k=1, so 0 != 1 is a mismatch, not a match.
Corrected table for f(1):
i | x | (x - i) & 1 | Matches k=1? | Action | Adjusted x |
|---|---|---|---|---|---|
| 0 | 3 | 1 | Yes | no change | 3 |
| 1 | 7 | 0 | No | mismatch; x == mx (7) → decrease | 6 |
| 2 | 5 | 1 | Yes | no change | 5 |
| 3 | 4 | 1 | Yes | no change | 4 |
So cnt = 1. Adjusted array is [3, 6, 5, 4], which alternates (odd, even, odd, even). ✓
Tracking extremes on [3, 6, 5, 4]:
a = min(3, 6, 5, 4) = 3b = max(3, 6, 5, 4) = 6
Spread = max(1, 6 - 3) = 3.
So f(1) = [1, 3].
Step 5: Compare the two shapes.
We return min(f(0), f(1)) = min([4, 3], [1, 3]).
Comparing element by element: the first elements are 4 vs 1, and 1 < 4, so f(1) wins immediately.
Result: answer = [1, 3].
This means we need a minimum of 1 operation (decrease the 7 at index 1 to 6), and the smallest achievable spread under that optimal operation count is 3. The greedy choice to decrease the maximum (rather than increase it to 8) kept the spread at 6 - 3 = 3 instead of 8 - 3 = 5.
Solution Implementation
1from math import inf
2from typing import List
3
4
5class Solution:
6 def makeParityAlternating(self, nums: List[int]) -> List[int]:
7 # Try to make the array "parity alternating" with respect to a target parity k.
8 # The goal: for each index i, the parity of (nums[i] - i) should equal k.
9 # Whenever it doesn't match, we count one change and adjust the value by +/- 1.
10 def check(target_parity: int) -> List[int]:
11 change_count = 0 # number of elements that need adjustment
12 cur_min = inf # tracks the minimum value after adjustments
13 cur_max = -inf # tracks the maximum value after adjustments
14
15 for index, value in enumerate(nums):
16 # If the parity of (value - index) doesn't match the target,
17 # this element needs to be modified.
18 if ((value - index) & 1) != target_parity:
19 change_count += 1
20 # Adjust the value by 1 while staying within the original bounds:
21 # if it equals the global minimum, push it up; if it equals the
22 # global maximum, pull it down.
23 if value == global_min:
24 value += 1
25 elif value == global_max:
26 value -= 1
27
28 cur_min = min(cur_min, value)
29 cur_max = max(cur_max, value)
30
31 # Return the cost (number of changes) and the resulting range,
32 # ensuring the range is at least 1.
33 return [change_count, max(1, cur_max - cur_min)]
34
35 # A single-element array is trivially alternating with zero cost and zero range.
36 if len(nums) == 1:
37 return [0, 0]
38
39 global_min, global_max = min(nums), max(nums)
40 # Compare both target parities (0 and 1) and pick the better result.
41 return min(check(0), check(1))
421class Solution {
2 // The input array we operate on
3 private int[] nums;
4 // The minimum value found in the array
5 private int min;
6 // The maximum value found in the array
7 private int max;
8 // A "large" sentinel value used to avoid integer overflow during min/max tracking
9 private static final int INF = Integer.MAX_VALUE / 2;
10
11 public int[] makeParityAlternating(int[] nums) {
12 // A single-element array is already valid: zero changes, range treated as 0
13 if (nums.length == 1) {
14 return new int[] {0, 0};
15 }
16 this.nums = nums;
17
18 // Compute the global minimum and maximum of the array
19 min = INF;
20 max = -INF;
21 for (int value : nums) {
22 min = Math.min(min, value);
23 max = Math.max(max, value);
24 }
25
26 // Evaluate both possible parity patterns:
27 // k = 0 -> positions where (value - index) is even
28 // k = 1 -> positions where (value - index) is odd
29 int[] result0 = f(0);
30 int[] result1 = f(1);
31
32 // Prefer the pattern requiring fewer changes;
33 // if equal, prefer the one with the smaller resulting range
34 if (result0[0] != result1[0]) {
35 return result0[0] < result1[0] ? result0 : result1;
36 }
37 return result0[1] <= result1[1] ? result0 : result1;
38 }
39
40 /**
41 * Evaluates the cost and resulting range for a target parity pattern.
42 *
43 * @param k the target parity of (value - index): 0 for even, 1 for odd
44 * @return an array {changeCount, range}, where changeCount is the number of
45 * elements that must be adjusted and range is the max-min spread
46 * (at least 1) of the values after adjustment
47 */
48 private int[] f(int k) {
49 // Number of elements that violate the target parity and must be changed
50 int count = 0;
51 // Track the minimum value after potential adjustments
52 int low = INF;
53 // Track the maximum value after potential adjustments
54 int high = -INF;
55
56 for (int i = 0; i < nums.length; i++) {
57 int x = nums[i];
58 // Check whether (x - i) matches the desired parity k
59 if (((x - i) & 1) != k) {
60 count++;
61 // Adjust the value by 1 to fix the parity,
62 // nudging boundary values inward to keep the range tight
63 if (x == min) {
64 x += 1;
65 } else if (x == max) {
66 x -= 1;
67 }
68 }
69 low = Math.min(low, x);
70 high = Math.max(high, x);
71 }
72
73 // Ensure the reported range is at least 1
74 return new int[] {count, Math.max(1, high - low)};
75 }
76}
771class Solution {
2public:
3 vector<int> makeParityAlternating(vector<int>& nums) {
4 // Edge case: a single element requires no changes,
5 // and the spread is defined as 0 here.
6 if (nums.size() == 1) {
7 return {0, 0};
8 }
9
10 // Find the global minimum and maximum values in one pass.
11 auto [minIter, maxIter] = minmax_element(nums.begin(), nums.end());
12 int globalMin = *minIter;
13 int globalMax = *maxIter;
14
15 // Lambda that evaluates a target parity pattern.
16 // 'parity' is the expected value of (x - i) & 1 for every index i.
17 // It returns: { number_of_changes, resulting_spread }.
18 auto evaluate = [&](int parity) {
19 int changeCount = 0; // How many elements violate the pattern.
20 int spreadMin = INT_MAX; // Minimum value after adjustments.
21 int spreadMax = INT_MIN; // Maximum value after adjustments.
22
23 for (int i = 0; i < static_cast<int>(nums.size()); i++) {
24 int value = nums[i];
25
26 // If the current element's parity does not match the target,
27 // it must be adjusted (toggled) to fix the alternating pattern.
28 if (((value - i) & 1) != parity) {
29 changeCount++;
30
31 // Adjust the value inward so the spread is minimized:
32 // bump the global minimum up, or pull the global maximum down.
33 if (value == globalMin) {
34 ++value;
35 } else if (value == globalMax) {
36 --value;
37 }
38 }
39
40 // Track the running min/max of the (possibly adjusted) values.
41 spreadMin = min(spreadMin, value);
42 spreadMax = max(spreadMax, value);
43 }
44
45 // The spread is at least 1 to avoid a degenerate zero result.
46 return vector<int>{changeCount, max(1, spreadMax - spreadMin)};
47 };
48
49 // Try both possible target parities and pick the lexicographically
50 // smaller result (fewer changes first, then smaller spread).
51 vector<int> resultEven = evaluate(0);
52 vector<int> resultOdd = evaluate(1);
53
54 return resultEven < resultOdd ? resultEven : resultOdd;
55 }
56};
571/**
2 * Determines the optimal parity-alternating configuration for the given array.
3 *
4 * For each index i, the "target parity" is derived from (nums[i] - i). We try two
5 * possible alternating patterns (k = 0 and k = 1), counting how many elements break
6 * the pattern and tracking the resulting spread (max - min) after adjusting the
7 * boundary values (the current global min/max) by one step.
8 *
9 * The result with the smaller mismatch count wins; ties are broken by the smaller spread.
10 *
11 * @param nums - The input array of numbers.
12 * @returns A tuple [count, spread] describing the best configuration.
13 */
14function makeParityAlternating(nums: number[]): number[] {
15 // A single-element array trivially needs no changes and has zero spread.
16 if (nums.length === 1) {
17 return [0, 0];
18 }
19
20 // Precompute the global minimum and maximum of the array.
21 const minValue = Math.min(...nums);
22 const maxValue = Math.max(...nums);
23
24 /**
25 * Evaluates a single alternating pattern.
26 *
27 * @param targetParity - The desired parity (0 or 1) of (nums[i] - i).
28 * @returns A tuple [mismatchCount, spread] where:
29 * - mismatchCount is the number of elements violating the pattern,
30 * - spread is the value range (max - min), at least 1.
31 */
32 const evaluatePattern = (targetParity: number): number[] => {
33 let mismatchCount = 0;
34 let localMin = Number.MAX_SAFE_INTEGER;
35 let localMax = Number.MIN_SAFE_INTEGER;
36
37 for (let i = 0; i < nums.length; i++) {
38 let value = nums[i];
39
40 // Check whether this element violates the desired parity pattern.
41 if (((value - i) & 1) !== targetParity) {
42 mismatchCount++;
43
44 // Adjust boundary values by one step to reduce the spread when possible.
45 if (value === minValue) {
46 ++value;
47 } else if (value === maxValue) {
48 --value;
49 }
50 }
51
52 // Update the local minimum and maximum after any adjustment.
53 localMin = Math.min(localMin, value);
54 localMax = Math.max(localMax, value);
55 }
56
57 // The spread is at least 1 to avoid a degenerate zero range.
58 return [mismatchCount, Math.max(1, localMax - localMin)];
59 };
60
61 // Evaluate both possible alternating patterns.
62 const resultEven = evaluatePattern(0);
63 const resultOdd = evaluatePattern(1);
64
65 // Prefer the pattern with fewer mismatches.
66 if (resultEven[0] !== resultOdd[0]) {
67 return resultEven[0] < resultOdd[0] ? resultEven : resultOdd;
68 }
69
70 // On a tie in mismatch count, prefer the pattern with the smaller spread.
71 return resultEven[1] <= resultOdd[1] ? resultEven : resultOdd;
72}
73Time and Space Complexity
Time Complexity
The time complexity is O(n), where n is the length of the array nums.
The analysis is as follows:
- The function
f(k)iterates over the entire arraynumsonce using a single loop. Within each iteration, the operations performed (parity check(x - i) & 1, comparisons withmnandmx, and updatingaandbviamin/max) all take constant timeO(1). Therefore, a single call tof(k)costsO(n). - Before the calls, computing
mn, mx = min(nums), max(nums)requires traversing the array, which takesO(n). - The final statement
min(f(0), f(1))invokesftwice, costing2 * O(n) = O(n).
Combining these parts, the overall time complexity is O(n).
Space Complexity
The space complexity is O(1).
The analysis is as follows:
- The function only uses a constant number of extra variables, such as
cnt,a,b,mn,mx,i, andx, none of which scale with the input size. - Although
f(k)returns a list, it has a fixed length of2, contributing only constant space.
Therefore, the extra space used is constant, and the space complexity is O(1).
Pattern Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Forgetting the max(1, ...) guard on the spread
A very common mistake is computing the spread simply as cur_max - cur_min without the lower bound of 1. This breaks on inputs where, after adjustment, all elements collapse to the same value.
Why it's wrong:
Consider nums = [2, 2]. For target parity k = 0:
- Index
0, value2:(2 - 0) & 1 == 0 == k, so it stays2. - Index
1, value2:(2 - 1) & 1 == 1 != k, so it needs a change. Since2 == global_min(and alsoglobal_max), the code adjusts it.
If your raw computation ever yields cur_max - cur_min == 0, returning 0 is logically impossible for an array of length ≥ 2: adjacent elements must have different parity, so they can never be equal. The true minimum spread is 1.
Solution: Always clamp the spread with max(1, cur_max - cur_min) when n >= 2, as the reference code does. This reflects the mathematical fact that two adjacent, differently-parity integers differ by at least 1.
Pitfall 2: Using (value & 1) != target_parity instead of ((value - index) & 1)
A natural but incorrect attempt is to check the parity of value directly against target_parity, forgetting that the target parity itself alternates with the index.
Why it's wrong:
The requirement is that even and odd indices hold opposite parities. If you only compare value & 1 to a fixed target_parity, you'd force every element to the same parity, which is the opposite of "alternating." Subtracting the index (value - index) normalizes the alternation: as i increments, the expected parity flips, so (value - i) & 1 being constant across all indices is exactly the alternating condition.
Solution: Use ((value - index) & 1) != target_parity. The - index term encodes the alternation directly into a single comparison, letting one target_parity value describe an entire alternating shape.
Pitfall 3: Adjusting an element in a direction that worsens the spread
When an element needs its parity flipped, both +1 and -1 are valid (each flips parity and costs 1). A naive implementation might always do value += 1 (or always -1), which can needlessly enlarge the spread.
Why it's wrong:
If the element being adjusted is the current global minimum and you do value -= 1, you push the minimum even lower, enlarging cur_max - cur_min. Symmetrically, blindly increasing the global maximum enlarges the spread upward.
Solution: Choose the direction greedily based on position relative to the extremes:
- If
value == global_min, increase it (+1) to avoid lowering the floor. - If
value == global_max, decrease it (-1) to avoid raising the ceiling. - Otherwise, the value is strictly interior, so either direction keeps it within
[global_min, global_max]and doesn't affect the spread.
This greedy local choice guarantees the minimal possible spread among all valid arrays achievable with answer[0] operations.
Pitfall 4: Not handling the length-1 array as a special case
Skipping the early return for len(nums) == 1 causes the max(1, ...) guard to incorrectly report a spread of 1 for a single-element array, when the correct answer is [0, 0].
Why it's wrong:
A length-1 array is trivially parity alternating (there are no adjacent pairs to violate the condition), so 0 operations are needed and the spread is 0. The generic code path would return a spread of at least 1 due to the clamp.
Solution: Return [0, 0] immediately when len(nums) == 1, before invoking the general logic.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapConsider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!