2541. Minimum Operations to Make Array Equal II
Problem Description
You have two integer arrays nums1
and nums2
that are both the same length n
, and an integer k
.
You can perform the following operation on nums1
:
- Pick any two different indices
i
andj
- Add
k
tonums1[i]
- Subtract
k
fromnums1[j]
This operation maintains the total sum of nums1
since you're adding k
to one element and subtracting k
from another.
Your goal is to make nums1
exactly equal to nums2
, meaning every element at each index must match: nums1[i] == nums2[i]
for all indices from 0
to n-1
.
Return the minimum number of operations needed to achieve this. If it's impossible to make the arrays equal, return -1
.
For example, if nums1 = [4, 3, 1, 4]
, nums2 = [1, 3, 7, 1]
, and k = 3
:
- You could add
3
tonums1[0]
and subtract3
fromnums1[2]
, makingnums1 = [7, 3, -2, 4]
- Then add
3
tonums1[2]
twice and subtract3
fromnums1[3]
twice, eventually reachingnums1 = [1, 3, 7, 1]
The solution needs to handle edge cases like when k = 0
(no changes possible unless arrays are already equal) and when the differences between corresponding elements aren't divisible by k
(making transformation impossible).
Intuition
Let's think about what each operation actually does. When we add k
to nums1[i]
and subtract k
from nums1[j]
, we're essentially transferring k
units from position j
to position i
. The total sum of the array remains unchanged.
For each index i
, we need nums1[i]
to become nums2[i]
. The difference nums1[i] - nums2[i]
tells us how much excess or deficit we have at position i
. If this difference is positive, we have excess that needs to be removed. If negative, we have a deficit that needs to be filled.
Key observations:
-
Since each operation transfers exactly
k
units, any differencenums1[i] - nums2[i]
must be divisible byk
. If not, it's impossible to fix that position. -
The total sum of
nums1
never changes through our operations. Therefore, ifsum(nums1) != sum(nums2)
, it's impossible to make them equal. -
Every unit of excess at one position must be matched with a unit of deficit at another position. If we have
+3k
excess at positioni
, we need exactly-3k
deficit somewhere else to balance it out. -
To transfer
m*k
units from positionj
to positioni
, we need exactly|m|
operations. But here's the clever part: when we count the total operations needed, we're counting each transfer twice - once as removing excess from positionj
and once as filling deficit at positioni
. That's why the final answer istotal_transfers / 2
.
The special case k = 0
means no transfers are possible, so the arrays must already be equal at every position.
Solution Approach
The implementation uses a single-pass algorithm to check feasibility and calculate the minimum operations simultaneously.
We maintain two key variables:
x
: Tracks the cumulative difference betweennums1
andnums2
. This helps verify if the total sum matches.ans
: Accumulates the total absolute differences, which represents the total amount of transfers needed.
Algorithm Steps:
-
Iterate through both arrays simultaneously using
zip(nums1, nums2)
to process corresponding elements. -
Handle the special case
k = 0
:- If
k = 0
, no transfers are possible - If
a != b
at any position, return-1
immediately - Otherwise, continue to the next position
- If
-
Check divisibility for each difference:
- Calculate
(a - b)
for current position - If
(a - b) % k != 0
, the difference cannot be resolved with transfers of sizek
, so return-1
- Calculate
-
Track the transfers:
- Calculate
y = (a - b) // k
, which represents how many transfers of sizek
are needed - Add
abs(y)
toans
to count the total transfer amount - Add
y
(with sign) tox
to track the net difference
- Calculate
-
Final validation:
- After processing all positions, check if
x == 0
- If
x != 0
, the sums don't match, making transformation impossible, so return-1
- Otherwise, return
ans // 2
- After processing all positions, check if
Why ans // 2
?
Each transfer involves two positions - one giving and one receiving. When we sum up abs(y)
for all positions, we count each transfer twice: once as a positive difference (excess) and once as a negative difference (deficit). Therefore, the actual number of operations is half of the total absolute differences.
Time Complexity: O(n)
where n
is the length of the arrays
Space Complexity: O(1)
as we only use a constant amount of extra space
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 to illustrate the solution approach.
Example: nums1 = [3, 8, 5, 2]
, nums2 = [7, 2, 3, 6]
, k = 2
Step 1: Calculate differences at each position
- Position 0:
3 - 7 = -4
(need to add 4) - Position 1:
8 - 2 = 6
(need to remove 6) - Position 2:
5 - 3 = 2
(need to remove 2) - Position 3:
2 - 6 = -4
(need to add 4)
Step 2: Check feasibility
- All differences (-4, 6, 2, -4) are divisible by
k = 2
✓ - Sum of differences:
-4 + 6 + 2 + (-4) = 0
✓ - Total sum check:
sum(nums1) = 18
,sum(nums2) = 18
✓
Step 3: Calculate transfers needed
- Position 0:
-4 ÷ 2 = -2
transfers (needs 2 incoming transfers) - Position 1:
6 ÷ 2 = 3
transfers (needs 3 outgoing transfers) - Position 2:
2 ÷ 2 = 1
transfer (needs 1 outgoing transfer) - Position 3:
-4 ÷ 2 = -2
transfers (needs 2 incoming transfers)
Step 4: Count total transfer amount
- Total absolute transfers:
|-2| + |3| + |1| + |-2| = 2 + 3 + 1 + 2 = 8
Step 5: Calculate operations
- Since each operation moves
k
units from one position to another, and we counted each transfer twice (once as outgoing, once as incoming), the answer is8 ÷ 2 = 4
operations.
Verification of the operations:
- Transfer from position 1 to position 0:
nums1 = [5, 6, 5, 2]
- Transfer from position 1 to position 0:
nums1 = [7, 4, 5, 2]
- Transfer from position 1 to position 3:
nums1 = [7, 2, 5, 4]
- Transfer from position 2 to position 3:
nums1 = [7, 2, 3, 6]
After 4 operations, nums1
equals nums2
!
Solution Implementation
1class Solution:
2 def minOperations(self, nums1: List[int], nums2: List[int], k: int) -> int:
3 # Initialize total operations counter and net change tracker
4 total_operations = 0
5 net_change = 0
6
7 # Iterate through both arrays simultaneously
8 for num1_val, num2_val in zip(nums1, nums2):
9 # Special case: when k is 0, arrays must be identical
10 if k == 0:
11 if num1_val != num2_val:
12 return -1
13 continue
14
15 # Check if the difference is divisible by k
16 if (num1_val - num2_val) % k != 0:
17 return -1
18
19 # Calculate the number of k-operations needed for this index
20 operations_at_index = (num1_val - num2_val) // k
21
22 # Add absolute value to total operations count
23 total_operations += abs(operations_at_index)
24
25 # Track net change (positive operations vs negative operations)
26 net_change += operations_at_index
27
28 # If net change is not zero, operations don't balance out
29 if net_change != 0:
30 return -1
31
32 # Each operation affects two indices, so divide by 2
33 return total_operations // 2
34
1class Solution {
2 public long minOperations(int[] nums1, int[] nums2, int k) {
3 // Total number of operations needed (counting both additions and subtractions)
4 long totalOperations = 0;
5 // Net difference tracker to ensure balance between additions and subtractions
6 long netDifference = 0;
7
8 // Iterate through both arrays simultaneously
9 for (int i = 0; i < nums1.length; i++) {
10 int currentNum1 = nums1[i];
11 int currentNum2 = nums2[i];
12
13 // Special case: when k is 0, no operations are possible
14 if (k == 0) {
15 // If elements differ when k=0, transformation is impossible
16 if (currentNum1 != currentNum2) {
17 return -1;
18 }
19 // Elements are equal, continue to next pair
20 continue;
21 }
22
23 // Check if the difference between elements is divisible by k
24 // If not, we cannot transform currentNum1 to currentNum2
25 if ((currentNum1 - currentNum2) % k != 0) {
26 return -1;
27 }
28
29 // Calculate the number of operations needed for this position
30 // Positive means we need to subtract from nums1[i]
31 // Negative means we need to add to nums1[i]
32 int operationCount = (currentNum1 - currentNum2) / k;
33
34 // Add absolute value to total operations
35 totalOperations += Math.abs(operationCount);
36
37 // Track net difference to ensure conservation
38 // Sum should be 0 for valid transformation
39 netDifference += operationCount;
40 }
41
42 // Check if the operations balance out (what we add equals what we subtract)
43 // If balanced, return half of total operations (since each operation affects two elements)
44 // Otherwise, transformation is impossible
45 return netDifference == 0 ? totalOperations / 2 : -1;
46 }
47}
48
1class Solution {
2public:
3 long long minOperations(vector<int>& nums1, vector<int>& nums2, int k) {
4 long long totalOperations = 0; // Total number of operations needed
5 long long netDifference = 0; // Net difference to check balance
6
7 // Iterate through both arrays simultaneously
8 for (int i = 0; i < nums1.size(); ++i) {
9 int currentNum1 = nums1[i];
10 int currentNum2 = nums2[i];
11
12 // Special case: when k is 0, arrays must already be identical
13 if (k == 0) {
14 if (currentNum1 != currentNum2) {
15 return -1; // Cannot make arrays equal without operations
16 }
17 continue;
18 }
19
20 // Check if the difference is divisible by k
21 int difference = currentNum1 - currentNum2;
22 if (difference % k != 0) {
23 return -1; // Cannot make elements equal with operations of size k
24 }
25
26 // Calculate number of operations needed for this pair
27 int operationsForPair = difference / k;
28
29 // Accumulate absolute value (each operation counts)
30 totalOperations += abs(operationsForPair);
31
32 // Track net difference (positive and negative should balance)
33 netDifference += operationsForPair;
34 }
35
36 // Check if operations balance out (sum of differences should be 0)
37 // If balanced, return half of total operations (each operation affects 2 elements)
38 return (netDifference == 0) ? totalOperations / 2 : -1;
39 }
40};
41
1/**
2 * Calculates the minimum number of operations to transform nums1 into nums2
3 * by adding/subtracting k from elements
4 * @param nums1 - First array of numbers
5 * @param nums2 - Second array of numbers (target)
6 * @param k - The value to add or subtract in each operation
7 * @returns Minimum number of operations, or -1 if impossible
8 */
9function minOperations(nums1: number[], nums2: number[], k: number): number {
10 const arrayLength: number = nums1.length;
11
12 // Special case: when k is 0, arrays must be identical
13 if (k === 0) {
14 const areArraysIdentical: boolean = nums1.every((value, index) => value === nums2[index]);
15 return areArraysIdentical ? 0 : -1;
16 }
17
18 // Track the total difference and absolute difference
19 let totalDifference: number = 0;
20 let totalAbsoluteDifference: number = 0;
21
22 // Process each element pair
23 for (let i = 0; i < arrayLength; i++) {
24 const difference: number = nums1[i] - nums2[i];
25
26 // Accumulate the signed difference
27 totalDifference += difference;
28
29 // Check if the difference is divisible by k
30 if (difference % k !== 0) {
31 return -1; // Impossible to transform if not divisible by k
32 }
33
34 // Accumulate the absolute difference
35 totalAbsoluteDifference += Math.abs(difference);
36 }
37
38 // The total difference must be zero for a valid transformation
39 if (totalDifference !== 0) {
40 return -1;
41 }
42
43 // Each operation affects two elements, so divide by 2k
44 return totalAbsoluteDifference / (k * 2);
45}
46
Time and Space Complexity
The time complexity of this solution is O(n)
, where n
is the length of the arrays nums1
and nums2
. This is because the algorithm iterates through both arrays exactly once using zip(nums1, nums2)
, performing constant-time operations for each pair of elements (arithmetic operations, modulo checks, and variable updates).
The space complexity is O(1)
as the algorithm only uses a fixed amount of extra space regardless of the input size. The variables ans
, x
, a
, b
, and y
are the only additional storage used, and their space requirements don't scale with the input size n
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Handle the k = 0 Special Case
Pitfall: When k = 0
, no operations are possible since adding/subtracting 0 doesn't change any values. Many solutions forget this edge case and might attempt division by zero or return incorrect results.
Example of incorrect handling:
# WRONG - This will cause ZeroDivisionError operations_at_index = (num1_val - num2_val) // k # Crashes when k = 0
Solution: Always check for k = 0
first and handle it separately:
if k == 0: if num1_val != num2_val: return -1 # Can't make arrays equal continue # Skip to next element if already equal
2. Not Checking if the Sum of Arrays Match
Pitfall: Since each operation adds k
to one element and subtracts k
from another, the total sum of nums1
must equal the total sum of nums2
for transformation to be possible. Failing to verify this leads to incorrect results.
Example scenario that would fail without sum checking:
nums1 = [1, 2, 3]
,nums2 = [2, 3, 5]
,k = 1
- Sum of nums1 = 6, Sum of nums2 = 10
- Without the net_change check, the algorithm might not detect this impossibility
Solution: Track the net change throughout the iteration and verify it equals zero at the end:
net_change += operations_at_index # After loop if net_change != 0: return -1
3. Forgetting to Divide the Total Operations by 2
Pitfall: Each operation involves two indices - one gains k
and one loses k
. When summing absolute differences, each operation gets counted twice (once as positive difference, once as negative).
Example of the double-counting issue:
nums1 = [5, 3]
,nums2 = [3, 5]
,k = 2
- Index 0 needs -1 operation (subtract 2), Index 1 needs +1 operation (add 2)
- Total absolute differences = |−1| + |1| = 2
- But this represents just ONE operation (moving 2 from index 0 to index 1)
Solution: Always divide the final count by 2:
return total_operations // 2 # Not just total_operations
4. Using Modulo Check Incorrectly with Negative Numbers
Pitfall: In Python, modulo with negative numbers works correctly, but conceptually some might check divisibility incorrectly or use absolute values unnecessarily.
Incorrect approach:
# WRONG - Using absolute value loses important sign information
if abs(num1_val - num2_val) % k != 0:
return -1
operations = abs(num1_val - num2_val) // k # Lost direction of transfer
Solution: Keep the sign information intact:
if (num1_val - num2_val) % k != 0: return -1 operations_at_index = (num1_val - num2_val) // k # Preserves sign for net_change tracking
A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".
What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?
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
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Want a Structured Path to Master System Design Too? Don’t Miss This!