Facebook Pixel

2449. Minimum Number of Operations to Make Arrays Similar

Problem Description

You have two positive integer arrays nums and target of equal length. Your goal is to make nums "similar" to target through a series of operations.

In each operation, you can:

  • Choose two different indices i and j (where 0 <= i, j < nums.length)
  • Add 2 to nums[i]
  • Subtract 2 from nums[j]

Two arrays are considered similar when they contain the same elements with the same frequencies. For example, [1, 2, 3] and [3, 1, 2] are similar because they both have one occurrence each of 1, 2, and 3.

The task is to find the minimum number of operations needed to transform nums into an array that is similar to target.

The key insight is that each operation maintains the sum of the array (adding 2 to one element and subtracting 2 from another keeps the total unchanged). Also, operations preserve the parity of elements - odd numbers stay odd and even numbers stay even, since we're always adding or subtracting 2.

The solution leverages these properties by:

  1. Separating elements by parity (odd and even)
  2. Sorting both arrays with the same criteria
  3. Pairing corresponding elements after sorting
  4. Calculating the total adjustments needed

Since each operation changes two elements by a combined difference of 4 (one increases by 2, another decreases by 2), the total difference divided by 4 gives us the minimum number of operations required.

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

Intuition

Let's think about what happens during each operation. When we add 2 to one element and subtract 2 from another, we're essentially transferring "2 units" between positions. This is like redistributing values while keeping the total sum constant.

The critical observation is that adding or subtracting 2 preserves parity - an odd number remains odd (odd ± 2 = odd), and an even number remains even (even ± 2 = even). This means we can never transform an odd number into an even number or vice versa through our operations.

Since we need the arrays to be similar (same elements with same frequencies), and we can't change parity, each odd number in nums must eventually become some odd number in target, and each even number in nums must become some even number in target.

This naturally divides our problem into two independent subproblems:

  • Transform all odd numbers in nums to match odd numbers in target
  • Transform all even numbers in nums to match even numbers in target

Now, how should we pair elements? If we have sorted odd numbers [1, 5, 9] in nums and [3, 7, 11] in target, the optimal pairing is to match them in order: 1→3, 5→7, 9→11. Why? Because any other pairing would create unnecessary "crossovers" that increase the total difference we need to fix.

For calculating the number of operations: when we transform element a to element b, we need |a - b| / 2 adjustments to that element. But remember, each operation affects two elements - one increases and one decreases. So when we sum up all the differences Σ|a - b|, we're counting each operation twice (once for the element that increases, once for the element that decreases). Additionally, since each operation changes values by 2, we divide by 2 again. This gives us Σ|a - b| / 4 as the total number of operations.

Learn more about Greedy and Sorting patterns.

Solution Approach

The implementation follows a clever sorting strategy to efficiently pair elements based on their parity:

Step 1: Custom Sorting

Both nums and target arrays are sorted using the same key function: lambda x: (x & 1, x)

  • x & 1 is a bitwise operation that returns 1 for odd numbers and 0 for even numbers
  • This creates a tuple (parity, value) for each element
  • Sorting by this tuple groups even numbers first (parity = 0), then odd numbers (parity = 1)
  • Within each group, numbers are sorted in ascending order

For example, if nums = [8, 3, 5, 2]:

  • After sorting: [2, 8, 3, 5] (evens first: 2, 8; then odds: 3, 5)

Step 2: Element Pairing

After sorting both arrays with the same criteria, we use zip(nums, target) to pair corresponding elements:

  • Even numbers in nums are paired with even numbers in target
  • Odd numbers in nums are paired with odd numbers in target
  • The sorted order ensures optimal pairing (smallest with smallest, etc.)

Step 3: Calculate Total Difference

We compute sum(abs(a - b) for a, b in zip(nums, target)):

  • For each pair (a, b), calculate the absolute difference |a - b|
  • Sum all these differences

Step 4: Determine Number of Operations

The final answer is the total difference divided by 4:

  • Each element needs |a - b| / 2 units of change
  • Since each operation affects two elements (one +2, one -2), and we're summing differences for all elements, we count each operation twice
  • Therefore, we divide by 4 to get the actual number of operations

Example Walkthrough:

If nums = [8, 12, 6] and target = [2, 14, 10]:

  1. After sorting: nums = [6, 8, 12], target = [2, 10, 14] (all even, so sorted by value)
  2. Pairs: (6, 2), (8, 10), (12, 14)
  3. Differences: |6-2| + |8-10| + |12-14| = 4 + 2 + 2 = 8
  4. Operations needed: 8 / 4 = 2

This approach ensures optimal pairing and correctly accounts for the bidirectional nature of each operation.

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 work through a concrete example to illustrate the solution approach.

Given:

  • nums = [1, 4, 7, 10]
  • target = [3, 8, 5, 12]

Step 1: Custom Sorting

First, we sort both arrays using the key lambda x: (x & 1, x):

For nums:

  • 1: (1 & 1, 1) = (1, 1) (odd)
  • 4: (4 & 1, 4) = (0, 4) (even)
  • 7: (7 & 1, 7) = (1, 7) (odd)
  • 10: (10 & 1, 10) = (0, 10) (even)

Sorted nums: [4, 10, 1, 7] (evens first: 4, 10; then odds: 1, 7)

For target:

  • 3: (3 & 1, 3) = (1, 3) (odd)
  • 8: (8 & 1, 8) = (0, 8) (even)
  • 5: (5 & 1, 5) = (1, 5) (odd)
  • 12: (12 & 1, 12) = (0, 12) (even)

Sorted target: [8, 12, 3, 5] (evens first: 8, 12; then odds: 3, 5)

Step 2: Element Pairing

After sorting, we pair corresponding elements:

  • Even pairs: (4, 8) and (10, 12)
  • Odd pairs: (1, 3) and (7, 5)

Step 3: Calculate Differences

For each pair, calculate the absolute difference:

  • |4 - 8| = 4
  • |10 - 12| = 2
  • |1 - 3| = 2
  • |7 - 5| = 2

Total difference: 4 + 2 + 2 + 2 = 10

Step 4: Calculate Operations

Number of operations = 10 / 4 = 2.5 → 2 operations (integer division)

Verification:

Let's verify this makes sense:

  • Operation 1: Add 2 to nums[0]=1 (becomes 3), subtract 2 from nums[2]=7 (becomes 5)
  • Operation 2: Add 2 to nums[1]=4 (becomes 6), subtract 2 from nums[3]=10 (becomes 8)
  • Operation 3: Add 2 to nums[1]=6 (becomes 8), subtract 2 from nums[3]=8 (becomes 6) ... continuing until we match the target frequencies

The actual operations would transform:

  • 4 → 8 (needs +4, so 2 operations where it gains)
  • 10 → 12 (needs +2, so 1 operation where it gains)
  • 1 → 3 (needs +2, so 1 operation where it gains)
  • 7 → 5 (needs -2, so 1 operation where it loses)

Since we need 2 operations total and each operation affects exactly 2 elements, this confirms our calculation.

Solution Implementation

1from typing import List
2
3class Solution:
4    def makeSimilar(self, nums: List[int], target: List[int]) -> int:
5        # Sort both arrays by parity (odd/even) first, then by value
6        # x & 1 returns 1 for odd numbers, 0 for even numbers
7        # This groups even numbers first, then odd numbers, each group sorted in ascending order
8        nums.sort(key=lambda x: (x & 1, x))
9        target.sort(key=lambda x: (x & 1, x))
10      
11        # Calculate the total number of operations needed
12        # Each operation involves adding/subtracting 2 to/from a pair of numbers
13        # The sum of absolute differences gives us twice the total operations needed
14        # (since each operation affects two numbers)
15        # Dividing by 4 gives us the minimum number of operations
16        total_difference = sum(abs(num - target_num) for num, target_num in zip(nums, target))
17      
18        return total_difference // 4
19
1class Solution {
2    public long makeSimilar(int[] nums, int[] target) {
3        // Sort both arrays to match elements by their relative positions
4        Arrays.sort(nums);
5        Arrays.sort(target);
6      
7        // Separate even and odd numbers from nums array
8        List<Integer> evenNums = new ArrayList<>();
9        List<Integer> oddNums = new ArrayList<>();
10      
11        // Separate even and odd numbers from target array
12        List<Integer> evenTarget = new ArrayList<>();
13        List<Integer> oddTarget = new ArrayList<>();
14      
15        // Partition nums array into even and odd lists
16        for (int num : nums) {
17            if (num % 2 == 0) {
18                evenNums.add(num);
19            } else {
20                oddNums.add(num);
21            }
22        }
23      
24        // Partition target array into even and odd lists
25        for (int num : target) {
26            if (num % 2 == 0) {
27                evenTarget.add(num);
28            } else {
29                oddTarget.add(num);
30            }
31        }
32      
33        // Calculate total difference for transformation
34        long totalDifference = 0;
35      
36        // Sum up absolute differences between corresponding even numbers
37        for (int i = 0; i < evenNums.size(); i++) {
38            totalDifference += Math.abs(evenNums.get(i) - evenTarget.get(i));
39        }
40      
41        // Sum up absolute differences between corresponding odd numbers
42        for (int i = 0; i < oddNums.size(); i++) {
43            totalDifference += Math.abs(oddNums.get(i) - oddTarget.get(i));
44        }
45      
46        // Divide by 4 because each operation involves +2/-2 on two elements
47        // and we count each difference twice (once for increase, once for decrease)
48        return totalDifference / 4;
49    }
50}
51
1class Solution {
2public:
3    long long makeSimilar(vector<int>& nums, vector<int>& target) {
4        // Sort both arrays to match elements optimally
5        sort(nums.begin(), nums.end());
6        sort(target.begin(), target.end());
7      
8        // Separate odd and even numbers from nums array
9        vector<int> numsOdd;
10        vector<int> numsEven;
11      
12        // Separate odd and even numbers from target array
13        vector<int> targetOdd;
14        vector<int> targetEven;
15      
16        // Partition nums into odd and even numbers
17        for (int value : nums) {
18            if (value & 1) {  // Check if number is odd using bitwise AND
19                numsOdd.emplace_back(value);
20            } else {
21                numsEven.emplace_back(value);
22            }
23        }
24      
25        // Partition target into odd and even numbers
26        for (int value : target) {
27            if (value & 1) {  // Check if number is odd using bitwise AND
28                targetOdd.emplace_back(value);
29            } else {
30                targetEven.emplace_back(value);
31            }
32        }
33      
34        // Calculate total difference for matching pairs
35        long long totalDifference = 0;
36      
37        // Sum absolute differences between corresponding odd numbers
38        for (int i = 0; i < numsOdd.size(); ++i) {
39            totalDifference += abs(numsOdd[i] - targetOdd[i]);
40        }
41      
42        // Sum absolute differences between corresponding even numbers
43        for (int i = 0; i < numsEven.size(); ++i) {
44            totalDifference += abs(numsEven[i] - targetEven[i]);
45        }
46      
47        // Divide by 4 because each operation changes two numbers by 2
48        // and we count each difference twice (once for increase, once for decrease)
49        return totalDifference / 4;
50    }
51};
52
1/**
2 * Calculates the minimum number of operations to make two arrays similar
3 * by swapping elements. Each operation consists of swapping two elements
4 * from different arrays that have the same parity (both even or both odd).
5 * 
6 * @param nums - First array of numbers to be transformed
7 * @param target - Target array that nums should be transformed into
8 * @returns The minimum number of operations needed
9 */
10function makeSimilar(nums: number[], target: number[]): number {
11    // Sort both arrays in ascending order for optimal pairing
12    nums.sort((a, b) => a - b);
13    target.sort((a, b) => a - b);
14
15    // Arrays to store even and odd numbers from nums
16    const evenNumbersFromNums: number[] = [];
17    const oddNumbersFromNums: number[] = [];
18  
19    // Arrays to store even and odd numbers from target
20    const evenNumbersFromTarget: number[] = [];
21    const oddNumbersFromTarget: number[] = [];
22
23    // Separate nums array into even and odd numbers
24    for (const value of nums) {
25        if (value % 2 === 0) {
26            evenNumbersFromNums.push(value);
27        } else {
28            oddNumbersFromNums.push(value);
29        }
30    }
31
32    // Separate target array into even and odd numbers
33    for (const value of target) {
34        if (value % 2 === 0) {
35            evenNumbersFromTarget.push(value);
36        } else {
37            oddNumbersFromTarget.push(value);
38        }
39    }
40
41    // Calculate total difference for transformation
42    let totalDifference = 0;
43  
44    // Sum up absolute differences between paired even numbers
45    for (let i = 0; i < evenNumbersFromNums.length; ++i) {
46        totalDifference += Math.abs(evenNumbersFromNums[i] - evenNumbersFromTarget[i]);
47    }
48
49    // Sum up absolute differences between paired odd numbers
50    for (let i = 0; i < oddNumbersFromNums.length; ++i) {
51        totalDifference += Math.abs(oddNumbersFromNums[i] - oddNumbersFromTarget[i]);
52    }
53
54    // Divide by 4 because each swap operation affects 2 elements
55    // and we count each difference twice (once from each perspective)
56    return totalDifference / 4;
57}
58

Time and Space Complexity

Time Complexity: O(n × log n), where n is the length of the array nums.

The time complexity is dominated by the sorting operations:

  • nums.sort(key=lambda x: (x & 1, x)) takes O(n × log n) time
  • target.sort(key=lambda x: (x & 1, x)) takes O(n × log n) time
  • The zip operation and sum calculation take O(n) time

Since the sorting operations are performed sequentially, the overall time complexity is O(n × log n) + O(n × log n) + O(n) = O(n × log n).

Space Complexity: O(1) or O(n) depending on the sorting algorithm implementation.

  • If the sorting is done in-place (like Python's Timsort for lists), the space complexity is O(1) for the additional variables
  • However, Python's sort may use up to O(n) auxiliary space in the worst case for its merge operations
  • The lambda functions and the generator expression in sum use O(1) additional space

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Incorrect Pairing Without Considering Parity

The Problem: A common mistake is to simply sort both arrays by value and pair them directly, without considering the parity constraint. Since operations only add/subtract 2, odd numbers can never become even and vice versa.

Incorrect Approach:

def makeSimilar(self, nums: List[int], target: List[int]) -> int:
    nums.sort()  # Wrong: sorts only by value
    target.sort()
    return sum(abs(a - b) for a, b in zip(nums, target)) // 4

Why It Fails: If nums = [1, 4, 5, 8] and target = [2, 3, 6, 7]:

  • Incorrect pairing: (1,2), (4,3), (5,6), (8,7)
  • This tries to transform odd→even and even→odd, which is impossible

Solution: Always separate by parity first using key=lambda x: (x & 1, x) or key=lambda x: (x % 2, x).

Pitfall 2: Wrong Division Factor

The Problem: Dividing by 2 instead of 4 when calculating the final answer.

Incorrect Approach:

def makeSimilar(self, nums: List[int], target: List[int]) -> int:
    nums.sort(key=lambda x: (x & 1, x))
    target.sort(key=lambda x: (x & 1, x))
    return sum(abs(a - b) for a, b in zip(nums, target)) // 2  # Wrong!

Why It Fails: Each operation changes two elements: one by +2 and another by -2. When summing absolute differences, we count the effect of each operation twice (once for the element that increases, once for the element that decreases). The total difference represents the sum of all individual changes needed, where each unit of change is 2. Since each operation provides 4 units of total change (2 up + 2 down), we divide by 4.

Solution: Remember that the division factor is 4 because:

  • Each element needs |a - b| units of adjustment
  • Each operation provides 4 units of total adjustment (2 added + 2 subtracted)
  • The sum counts each operation's effect twice

Pitfall 3: Using Modulo Instead of Bitwise AND

The Problem: While both x % 2 and x & 1 work correctly for determining parity, using modulo can be slightly less efficient for large datasets.

Less Optimal:

nums.sort(key=lambda x: (x % 2, x))  # Works but slightly slower

Optimal:

nums.sort(key=lambda x: (x & 1, x))  # Bitwise operation is faster

Why It Matters: Bitwise AND (&) is a single CPU instruction, while modulo (%) might require division. Though the performance difference is minimal in practice, using bitwise operations demonstrates better understanding of low-level optimizations.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which technique can we use to find the middle of a linked list?


Recommended Readings

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

Load More