Facebook Pixel

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] and nums[n-i-1] (elements equidistant from the ends), their absolute difference equals X
  • This means abs(nums[i] - nums[n-i-1]) = X for all valid indices i from 0 to n-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 difference X = 2:
    • Pair (nums[0], nums[3]) = (1, 3) has difference 2
    • Pair (nums[1], nums[2]) = (5, 3) has difference 2
    • No changes needed if this is already satisfied

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. If target difference = y - x: We don't need to change anything! The pair already has this difference.

  2. 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 to 0: creates difference y - 0 = y
    • Change y to k: creates difference k - x
    • So with one change, we can achieve any difference up to max(y, k - x)
  3. 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:

  1. Range [0, y-x-1]: needs 1 change

    d[0] += 1
    d[y - x] -= 1

    This adds 1 to all positions from 0 to y-x-1.

  2. Position [y-x]: needs 0 changes (already satisfied)

    • No update needed as the difference between the additions cancels out.
  3. Range [y-x+1, max(y, k-x)]: needs 1 change

    d[y - x + 1] += 1
    d[max(y, k - x) + 1] -= 1

    This adds 1 to all positions from y-x+1 to max(y, k-x).

  4. Range [max(y, k-x)+1, k]: needs 2 changes

    d[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 choose X = 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 Evaluator

Example 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
  • 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)
  • 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
  • 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] and nums[-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 size k + 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, and min() 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))
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm should you use to find a node that is close to the root of the tree?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More