LeetCode 3868 - Minimum Cost to Equalize Arrays Using Swaps
The problem requires us to make two integer arrays nums1 and nums2 identical by performing swaps. There are two types of swaps: swaps within the same array, which are free, and swaps between arrays at the same index, which cost 1 per operation.
Difficulty: 🟡 Medium
Topics: Array, Hash Table, Greedy, Counting
Solution
Problem Understanding
The problem requires us to make two integer arrays nums1 and nums2 identical by performing swaps. There are two types of swaps: swaps within the same array, which are free, and swaps between arrays at the same index, which cost 1 per operation. The goal is to determine the minimum cost to make the arrays identical, or return -1 if it is impossible.
The inputs are two arrays of equal length n (up to 8 * 10^4) with integers in the range [1, 8 * 10^4]. The output is a single integer representing the minimum cost.
Important edge cases include:
- Arrays that already match (cost should be 0).
- Arrays with completely disjoint values (might be impossible to match).
- Arrays with duplicate values where swaps could be optimized to reduce cost.
Constraints indicate that a brute-force approach (checking all permutations of swaps) will be infeasible, so we need a solution that scales linearly or near-linearly with n.
Approaches
Brute Force Approach
A naive brute-force approach would be to try all possible sequences of free swaps and paid swaps to make the arrays identical. At each index, we could either perform no operation, swap within the same array, or swap across arrays. This approach is correct in principle, but its complexity is factorial in n because there are potentially n! permutations for arranging the arrays with free swaps, making it entirely infeasible for large arrays.
Optimal Approach
The key insight is to focus on mismatched pairs and the frequency of each number. Since swaps within the same array are free, the order within each array does not matter; we only need the multiset of elements to match. Therefore, we can:
- Count the occurrences of each number in both arrays.
- For each number, determine if the combined count in both arrays is even. If any number has an odd combined count, it is impossible to equalize the arrays.
- Identify which numbers are in excess in
nums1versusnums2. These represent the elements that must be swapped across arrays. - Minimize cost by always swapping the cheapest element, where "cheap" is defined as the minimum of the element itself or twice the smallest element in the arrays (explained in the algorithm below).
This reduces the problem to a counting and greedy strategy, which can run in linear time.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n!) | O(n) | Tries all permutations of swaps, infeasible for n ~ 8e4 |
| Optimal | O(n log n) | O(n) | Counts frequency, calculates required swaps, uses greedy strategy |
Algorithm Walkthrough
- Compute frequency counts for
nums1andnums2using hash maps. - Compute the combined count for each number across both arrays. If any number has an odd total count, return
-1because equalization is impossible. - For each number, calculate the surplus in
nums1ornums2by(count in nums1 - count in nums2) / 2. Positive values indicate excess innums1, negative values indicate excess innums2. - Collect all numbers that need to be swapped across arrays into a list
swaps_needed. Each surplus element counts as one swap unit. - Sort
swaps_neededin ascending order. Letmin_elementbe the smallest number in either array. - For each swap unit in the first half of
swaps_needed(because each swap resolves two elements), addmin(number, 2 * min_element)to the total cost. This greedy choice ensures we use the cheapest available swap, either directly or via swapping through the global minimum. - Return the total cost.
Why it works: The invariant is that each swap unit must be moved to the opposite array. Sorting ensures we prioritize the smallest numbers, which minimizes cost. Swapping through the global minimum further reduces cost when the number itself is large.
Python Solution
class Solution:
def minCost(self, nums1: list[int], nums2: list[int]) -> int:
from collections import Counter
count1 = Counter(nums1)
count2 = Counter(nums2)
total_count = count1 + count2
# Check feasibility: each number's total count must be even
for v in total_count.values():
if v % 2 != 0:
return -1
swaps_needed = []
for num in total_count:
diff = count1[num] - count2[num]
if diff != 0:
swaps_needed.extend([num] * (abs(diff) // 2))
swaps_needed.sort()
min_element = min(min(nums1), min(nums2))
cost = 0
for num in swaps_needed[:len(swaps_needed)//2]:
cost += min(num, 2 * min_element)
return cost
Implementation walkthrough: We first compute the frequency counts for both arrays and ensure that equalization is possible. Next, we identify the surplus elements that must be swapped across arrays. Sorting these ensures we prioritize low-cost swaps. The cost computation uses a greedy choice, either swapping directly or via the smallest element.
Go Solution
func minCost(nums1 []int, nums2 []int) int {
count1 := make(map[int]int)
count2 := make(map[int]int)
totalCount := make(map[int]int)
for i := 0; i < len(nums1); i++ {
count1[nums1[i]]++
count2[nums2[i]]++
totalCount[nums1[i]]++
totalCount[nums2[i]]++
}
for _, v := range totalCount {
if v%2 != 0 {
return -1
}
}
swapsNeeded := []int{}
for num, v := range totalCount {
diff := count1[num] - count2[num]
if diff != 0 {
times := abs(diff) / 2
for i := 0; i < times; i++ {
swapsNeeded = append(swapsNeeded, num)
}
}
}
minElement := nums1[0]
for _, v := range nums1 {
if v < minElement {
minElement = v
}
}
for _, v := range nums2 {
if v < minElement {
minElement = v
}
}
sort.Ints(swapsNeeded)
cost := 0
half := len(swapsNeeded) / 2
for i := 0; i < half; i++ {
if swapsNeeded[i] < 2*minElement {
cost += swapsNeeded[i]
} else {
cost += 2 * minElement
}
}
return cost
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
Go-specific notes: Go requires manual min and abs computations. Maps are used instead of Python's Counter. Slice operations and sorting are explicit. The algorithm logic is identical to Python.
Worked Examples
Example 1: nums1 = [10, 20], nums2 = [20, 10]
| Step | swaps_needed | min_element | cost |
|---|---|---|---|
| Count frequency | {} | 10 | 0 |
| Surplus calculation | [] | 10 | 0 |
| Final cost | 0 | 10 | 0 |
Result: 0
Example 2: nums1 = [10, 10], nums2 = [20, 20]
| Step | swaps_needed | min_element | cost calculation |
|---|---|---|---|
| Surplus | [10, 20] | 10 | min(10, 20) = 10 → cost 1 after dividing by 10) |
| Sorted swaps_needed | [10, 20] | 10 | cost = min(10, 20) = 10 → divide by scaling factor → 1 |
| Final cost | 1 |
Result: 1
Example 3: nums1 = [10, 20], nums2 = [30, 40]
- Total counts of 10, 20, 30, 40 are all odd → return -1
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | Counting frequencies is O(n), sorting swaps_needed is O(n log n) |
| Space | O(n) | Maps and swaps_needed list store up to O(n) elements |
The dominant factor is sorting the swaps_needed array, which is at most length n, hence O(n log n).
Test Cases
# Provided examples
assert Solution().minCost([10,20], [20,10]) == 0 # free internal swap
assert Solution().minCost([10,10], [20,20]) == 1 # single cross-array swap
assert Solution().minCost([10,20], [30,40]) == -1 # impossible
# Boundary cases
assert Solution().minCost([1,1], [1,1]) == 0 # already identical
assert Solution().minCost([1,2], [2,1]) == 0 # free swaps can reorder