3224. Minimum Array Changes to Make Differences Equal
Problem Description
You have an integer array nums
of even length n
and an integer k
.
You can modify the array by replacing any element with any integer from 0
to k
.
Your goal is to transform the array so that there exists some integer X
where:
- For every pair of elements
nums[i]
andnums[n-i-1]
(elements equidistant from the ends), their absolute difference equalsX
- This means
abs(nums[i] - nums[n-i-1]) = X
for all valid indicesi
from0
ton-1
Find the minimum number of changes needed to achieve this condition.
Example understanding:
- If
nums = [1, 5, 3, 3]
and we want all pairs to have differenceX = 2
:- Pair
(nums[0], nums[3])
=(1, 3)
has difference2
✓ - Pair
(nums[1], nums[2])
=(5, 3)
has difference2
✓ - No changes needed if this is already satisfied
- Pair
The challenge is finding which value of X
requires the fewest changes across all pairs, considering you can only replace elements with values from 0
to k
.
Intuition
The key insight is that we need to choose a target difference X
that minimizes the total changes across all pairs. Instead of trying every possible X
and counting changes for each, we can be smarter by analyzing how many changes each pair needs for different values of X
.
For each pair (nums[i], nums[n-i-1])
, let's call the smaller value x
and the larger value y
. The current difference is y - x
.
Now, think about how many changes this pair needs for different target differences:
-
If target difference =
y - x
: We don't need to change anything! The pair already has this difference. -
If target difference is achievable with one change: We can change either element. The maximum difference we can create by changing one element is:
- Change
x
to0
: creates differencey - 0 = y
- Change
y
tok
: creates differencek - x
- So with one change, we can achieve any difference up to
max(y, k - x)
- Change
-
If target difference >
max(y, k - x)
: We must change both elements.
This gives us a pattern for each pair:
- Differences in range
[0, y-x-1]
: need 1 change - Difference exactly at
y-x
: need 0 changes - Differences in range
[y-x+1, max(y, k-x)]
: need 1 change - Differences in range
[max(y, k-x)+1, k]
: need 2 changes
Since we want to track how many changes are needed for each possible X
value across all pairs, we can use a difference array. For each pair, we update the ranges to add the required number of changes. After processing all pairs, the prefix sum of the difference array tells us the total changes needed for each possible X
, and we pick the minimum.
The difference array technique efficiently handles range updates in O(1)
time per update, making the overall solution linear in time complexity.
Learn more about Prefix Sum patterns.
Solution Approach
We implement the solution using a difference array to efficiently track the number of changes needed for each possible target difference X
from 0
to k
.
Step 1: Initialize the difference array
d = [0] * (k + 2)
We create a difference array of size k + 2
to handle all possible differences from 0
to k
, with an extra position for boundary handling.
Step 2: Process each pair of elements
For each pair at positions i
and n-i-1
:
for i in range(n // 2):
x, y = nums[i], nums[-i - 1]
if x > y:
x, y = y, x # Ensure x is the smaller value
Step 3: Apply range updates using the difference array
For each pair with values (x, y)
where x ≤ y
:
-
Range
[0, y-x-1]
: needs 1 changed[0] += 1 d[y - x] -= 1
This adds 1 to all positions from
0
toy-x-1
. -
Position
[y-x]
: needs 0 changes (already satisfied)- No update needed as the difference between the additions cancels out.
-
Range
[y-x+1, max(y, k-x)]
: needs 1 changed[y - x + 1] += 1 d[max(y, k - x) + 1] -= 1
This adds 1 to all positions from
y-x+1
tomax(y, k-x)
. -
Range
[max(y, k-x)+1, k]
: needs 2 changesd[max(y, k - x) + 1] += 2
This adds 2 more to positions beyond
max(y, k-x)
.
Step 4: Find the minimum using prefix sum
return min(accumulate(d))
The accumulate
function computes the prefix sum of the difference array, giving us the actual number of changes needed for each possible X
. We return the minimum value, which represents the minimum number of changes required.
Why this works:
- The difference array allows us to perform range updates in
O(1)
time - After all updates, the prefix sum at position
i
represents the total number of changes needed if we chooseX = i
- By taking the minimum across all prefix sums, we find the optimal
X
that minimizes changes
Time Complexity: O(n)
- we process n/2
pairs and compute prefix sums in linear time
Space Complexity: O(k)
- for the difference array
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 a concrete example with nums = [1, 0, 1, 2, 4, 3]
and k = 4
.
Step 1: Identify pairs
- Pair 1:
(nums[0], nums[5])
=(1, 3)
→ sorted as(x=1, y=3)
- Pair 2:
(nums[1], nums[4])
=(0, 4)
→ sorted as(x=0, y=4)
- Pair 3:
(nums[2], nums[3])
=(1, 2)
→ sorted as(x=1, y=2)
Step 2: Initialize difference array
d = [0, 0, 0, 0, 0, 0]
(size k+2 = 6)
Step 3: Process each pair
Pair 1: (x=1, y=3)
- Current difference:
3-1 = 2
- Max achievable with 1 change:
max(3, 4-1) = 3
- Updates:
- Range [0,1]: needs 1 change →
d[0] += 1, d[2] -= 1
- Position [2]: needs 0 changes (already satisfied)
- Range [3,3]: needs 1 change →
d[3] += 1, d[4] -= 1
- Range [4,4]: needs 2 changes →
d[4] += 2
- Range [0,1]: needs 1 change →
- After:
d = [1, 0, -1, 1, 1, 0]
Pair 2: (x=0, y=4)
- Current difference:
4-0 = 4
- Max achievable with 1 change:
max(4, 4-0) = 4
- Updates:
- Range [0,3]: needs 1 change →
d[0] += 1, d[4] -= 1
- Position [4]: needs 0 changes (already satisfied)
- Range [5,4]: empty (no update needed)
- Range [5,∞]: would need 2 changes (but out of bounds)
- Range [0,3]: needs 1 change →
- After:
d = [2, 0, -1, 1, 0, 0]
Pair 3: (x=1, y=2)
- Current difference:
2-1 = 1
- Max achievable with 1 change:
max(2, 4-1) = 3
- Updates:
- Range [0,0]: needs 1 change →
d[0] += 1, d[1] -= 1
- Position [1]: needs 0 changes (already satisfied)
- Range [2,3]: needs 1 change →
d[2] += 1, d[4] -= 1
- Range [4,4]: needs 2 changes →
d[4] += 2
- Range [0,0]: needs 1 change →
- After:
d = [3, -1, 0, 1, 1, 0]
Step 4: Compute prefix sums
prefix_sum = [3, 2, 2, 3, 4, 4]
This means:
- X=0: needs 3 changes
- X=1: needs 2 changes
- X=2: needs 2 changes
- X=3: needs 3 changes
- X=4: needs 4 changes
Result: Minimum is 2 changes (achieved with either X=1 or X=2)
To verify X=1 works with 2 changes:
- Pair (1,3): change 3→2 to get difference 1 ✓ (1 change)
- Pair (0,4): change 4→1 to get difference 1 ✓ (1 change)
- Pair (1,2): already has difference 1 ✓ (0 changes)
- Total: 2 changes ✓
Solution Implementation
1class Solution:
2 def minChanges(self, nums: List[int], k: int) -> int:
3 # Initialize difference array for tracking change costs
4 # Size is k+2 to handle boundary cases
5 difference_array = [0] * (k + 2)
6 n = len(nums)
7
8 # Process each pair of elements that should be equal in a k-periodic array
9 # For k-periodic array: nums[i] should equal nums[i+k]
10 for i in range(n // 2):
11 # Get the pair of elements that need to be considered
12 left_value = nums[i]
13 right_value = nums[n - i - 1]
14
15 # Ensure left_value <= right_value for consistent processing
16 if left_value > right_value:
17 left_value, right_value = right_value, left_value
18
19 # Update difference array based on the cost of making this pair equal
20 # with different target values
21
22 # Cost increases by 1 for all positions initially
23 difference_array[0] += 1
24
25 # If target equals the difference, no change needed for this pair
26 difference_array[right_value - left_value] -= 1
27 difference_array[right_value - left_value + 1] += 1
28
29 # Beyond a certain threshold, we need 2 changes instead of 1
30 max_single_change = max(right_value, k - left_value) + 1
31 difference_array[max_single_change] -= 1
32 difference_array[max_single_change] += 2
33
34 # Use accumulate to get prefix sums and find minimum cost
35 from itertools import accumulate
36 return min(accumulate(difference_array))
37
1class Solution {
2 public int minChanges(int[] nums, int k) {
3 // Create a difference array for tracking cost changes
4 // Size is k+2 to handle all possible target differences (0 to k) plus boundary
5 int[] costDifference = new int[k + 2];
6 int n = nums.length;
7
8 // Process each pair from start and end of the array
9 for (int i = 0; i < n / 2; ++i) {
10 // Get the pair: nums[i] and nums[n-i-1]
11 int smaller = Math.min(nums[i], nums[n - i - 1]);
12 int larger = Math.max(nums[i], nums[n - i - 1]);
13 int currentDifference = larger - smaller;
14
15 // Calculate the maximum achievable difference for this pair
16 // We can change one element to achieve any difference up to this maximum
17 int maxAchievableDifference = Math.max(larger, k - smaller);
18
19 // Update the difference array using range updates:
20 // For target difference 0 to currentDifference-1: need 1 change
21 costDifference[0] += 1;
22 costDifference[currentDifference] -= 1;
23
24 // For target difference equal to currentDifference: need 0 changes
25 costDifference[currentDifference] += 1;
26 costDifference[currentDifference + 1] -= 1;
27
28 // For target difference currentDifference+1 to maxAchievableDifference: need 1 change
29 costDifference[currentDifference + 1] += 1;
30 costDifference[maxAchievableDifference + 1] -= 1;
31
32 // For target difference > maxAchievableDifference: need 2 changes
33 costDifference[maxAchievableDifference + 1] += 2;
34 }
35
36 // Find the minimum cost by processing the difference array
37 int minChangesNeeded = n;
38 int cumulativeCost = 0;
39
40 for (int cost : costDifference) {
41 cumulativeCost += cost;
42 minChangesNeeded = Math.min(minChangesNeeded, cumulativeCost);
43 }
44
45 return minChangesNeeded;
46 }
47}
48
1class Solution {
2public:
3 int minChanges(vector<int>& nums, int k) {
4 // Create a difference array to track the cost of operations
5 // Size is k+2 to handle boundary cases
6 int costDifference[k + 2];
7 memset(costDifference, 0, sizeof(costDifference));
8
9 int n = nums.size();
10
11 // Process each pair from opposite ends of the array
12 for (int i = 0; i < n / 2; ++i) {
13 // Get the pair elements: left from start, right from end
14 int leftElement = nums[i];
15 int rightElement = nums[n - i - 1];
16
17 // Order the pair elements (smaller first, larger second)
18 int minValue = min(leftElement, rightElement);
19 int maxValue = max(leftElement, rightElement);
20
21 // Calculate the current difference between pair elements
22 int currentDiff = maxValue - minValue;
23
24 // Build difference array using range updates:
25 // [0, currentDiff): cost is 1 (need to change one element)
26 costDifference[0] += 1;
27 costDifference[currentDiff] -= 1;
28
29 // [currentDiff, currentDiff]: cost is 0 (already equal with this difference)
30 // No additional update needed due to previous -1
31
32 // [currentDiff + 1, maxPossibleDiff]: cost is 1 (change one element)
33 costDifference[currentDiff + 1] += 1;
34 int maxPossibleDiff = max(maxValue, k - minValue);
35 costDifference[maxPossibleDiff + 1] -= 1;
36
37 // Beyond maxPossibleDiff: cost is 2 (must change both elements)
38 costDifference[maxPossibleDiff + 1] += 2;
39 }
40
41 // Find the minimum cost by processing the difference array
42 int minCost = n; // Initialize with maximum possible cost
43 int currentCost = 0;
44
45 // Accumulate the difference array to get actual costs at each position
46 for (int diffValue : costDifference) {
47 currentCost += diffValue;
48 minCost = min(minCost, currentCost);
49 }
50
51 return minCost;
52 }
53};
54
1/**
2 * Finds the minimum number of changes needed to make all pairs (nums[i], nums[n-1-i])
3 * have the same absolute difference, where each element can be changed to any value in [0, k]
4 * @param nums - The input array of numbers
5 * @param k - The maximum value any element can be changed to
6 * @returns The minimum number of changes required
7 */
8function minChanges(nums: number[], k: number): number {
9 // Initialize difference array for tracking cost changes at each possible difference value
10 // Size is k+2 to handle boundary cases
11 const costDifferenceArray: number[] = Array(k + 2).fill(0);
12 const arrayLength: number = nums.length;
13
14 // Process each pair from the start and end of the array
15 for (let pairIndex = 0; pairIndex < arrayLength >> 1; ++pairIndex) {
16 // Get the smaller and larger values of the current pair
17 const minValue: number = Math.min(nums[pairIndex], nums[arrayLength - 1 - pairIndex]);
18 const maxValue: number = Math.max(nums[pairIndex], nums[arrayLength - 1 - pairIndex]);
19
20 // Current difference between the pair
21 const currentDifference: number = maxValue - minValue;
22
23 // Mark cost changes using difference array technique:
24 // - From 0 to currentDifference-1: need 1 change
25 costDifferenceArray[0] += 1;
26 costDifferenceArray[currentDifference] -= 1;
27
28 // - At currentDifference: need 0 changes (already satisfied)
29 costDifferenceArray[currentDifference + 1] += 1;
30
31 // - Beyond maximum achievable difference: need 2 changes
32 const maxAchievableDifference: number = Math.max(maxValue, k - minValue);
33 costDifferenceArray[maxAchievableDifference + 1] -= 1;
34 costDifferenceArray[maxAchievableDifference + 1] += 2;
35 }
36
37 // Find the minimum cost by calculating prefix sum
38 let minimumChanges: number = arrayLength;
39 let currentCost: number = 0;
40
41 for (const costChange of costDifferenceArray) {
42 currentCost += costChange;
43 minimumChanges = Math.min(minimumChanges, currentCost);
44 }
45
46 return minimumChanges;
47}
48
Time and Space Complexity
Time Complexity: O(n)
The algorithm iterates through half of the array (n/2
iterations) in the main loop. Within each iteration, all operations are constant time O(1)
:
- Array access operations:
nums[i]
andnums[-i - 1]
- Comparison and swap:
if x > y
- Difference array updates: updating
d
at specific indices max()
function with two arguments
After the loop, accumulate(d)
performs a prefix sum operation on the difference array d
of size k + 2
, which takes O(k)
time. The min()
function then finds the minimum in O(k)
time.
Since k
is given as a constraint and typically k ≤ n
, the overall time complexity is O(n + k) = O(n)
.
Space Complexity: O(k)
The algorithm uses:
- A difference array
d
of sizek + 2
:O(k)
space - A few constant variables (
x
,y
,n
,i
):O(1)
space - The
accumulate()
function creates an iterator that generates values on-the-fly, andmin()
processes them without storing all values simultaneously:O(1)
additional space
Therefore, the total space complexity is O(k)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Problem Goal
Many people initially think we need to make pairs equal (i.e., nums[i] == nums[n-i-1]
), but the problem actually requires all pairs to have the same absolute difference X
. This means |nums[i] - nums[n-i-1]| = X
for all pairs, where X
can be any value from 0 to k.
Wrong interpretation:
# Trying to make all pairs equal (X must be 0)
for i in range(n // 2):
if nums[i] != nums[n-i-1]:
changes += 1
Correct approach:
# Find the X that minimizes changes across all pairs # X can be any value from 0 to k
2. Incorrect Range Calculation for Single Change
When determining if we can achieve a target difference X
with one change, we need to consider what values are reachable. The maximum achievable difference with one change is max(y, k-x)
, not simply k
.
Common mistake:
# Incorrectly assuming we can always reach difference k with one change d[y - x + 1] += 1 d[k + 1] -= 1 # Wrong!
Correct calculation:
# The maximum difference achievable with one change
max_diff_one_change = max(y, k - x)
d[y - x + 1] += 1
d[max_diff_one_change + 1] -= 1
3. Off-by-One Errors in Difference Array Updates
The difference array technique requires careful attention to boundaries. Remember that d[i] += val
affects all positions from i
onwards until a corresponding d[j] -= val
.
Incorrect boundary:
# Wrong: This would include y-x in the range needing 1 change d[0] += 1 d[y - x + 1] -= 1 # Should be d[y - x] -= 1
Correct boundaries:
# Range [0, y-x-1] needs 1 change d[0] += 1 d[y - x] -= 1 # Position y-x needs 0 changes (natural difference) # Range [y-x+1, max_achievable] needs 1 change d[y - x + 1] += 1
4. Not Handling the Difference Array Size Properly
The difference array should be size k + 2
to handle all possible differences from 0 to k, plus an extra position for boundary handling.
Insufficient size:
d = [0] * (k + 1) # Can cause index out of bounds
Correct size:
d = [0] * (k + 2) # Handles all cases including boundary at k+1
5. Forgetting to Import accumulate or Using It Incorrectly
The solution relies on itertools.accumulate
to compute prefix sums efficiently.
Missing import or wrong usage:
# Forgot to import
return min(accumulate(d)) # NameError
# Or manual implementation with potential bugs
prefix_sum = []
curr = 0
for val in d:
curr += val
prefix_sum.append(curr)
return min(prefix_sum[:k+1]) # Might miss valid positions
Correct usage:
from itertools import accumulate
return min(accumulate(difference_array))
Which algorithm should you use to find a node that is close to the root of the tree?
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!