1818. Minimum Absolute Sum Difference
Problem Description
You have two positive integer arrays nums1
and nums2
, both with the same length n
.
The absolute sum difference between these arrays is calculated by summing up |nums1[i] - nums2[i]|
for all indices i
from 0
to n-1
.
Your task is to minimize this absolute sum difference by replacing at most one element in nums1
. You can replace any element in nums1
with any other element that already exists in nums1
.
For example, if nums1 = [1, 7, 5]
and nums2 = [2, 3, 5]
:
- The original absolute sum difference is
|1-2| + |7-3| + |5-5| = 1 + 4 + 0 = 5
- You could replace
nums1[1] = 7
withnums1[0] = 1
to get[1, 1, 5]
- The new absolute sum difference would be
|1-2| + |1-3| + |5-5| = 1 + 2 + 0 = 3
Return the minimum possible absolute sum difference after performing at most one replacement. Since the answer might be very large, return it modulo 10^9 + 7
.
The absolute value |x|
is defined as:
x
ifx >= 0
-x
ifx < 0
Intuition
The key insight is that we want to find the single replacement that gives us the maximum reduction in the total sum.
Let's think about what happens when we replace nums1[i]
with some other value from nums1
. The only term in the total sum that changes is |nums1[i] - nums2[i]|
. If we replace nums1[i]
with a different value x
(where x
must already exist in nums1
), this term becomes |x - nums2[i]|
.
The reduction we get from this replacement is:
original difference - new difference = |nums1[i] - nums2[i]| - |x - nums2[i]|
To maximize our reduction, we want to find the value x
in nums1
that minimizes |x - nums2[i]|
. In other words, we want to find the element in nums1
that is closest to nums2[i]
.
This naturally leads us to a binary search approach:
- First, calculate the total absolute sum difference without any replacements
- For each position
i
, find the element innums1
that is closest tonums2[i]
- Calculate how much we can reduce the sum by making this replacement
- Track the maximum possible reduction across all positions
- Subtract this maximum reduction from the original sum
To efficiently find the closest element, we can sort a copy of nums1
and use binary search. For each nums2[i]
, we use bisect_left
to find where nums2[i]
would fit in the sorted array. The closest element must be either at this position or the position just before it.
The final answer is (original_sum - max_reduction) % mod
.
Learn more about Binary Search and Sorting patterns.
Solution Approach
The solution follows a sorting and binary search strategy to find the optimal replacement:
Step 1: Initial Setup
- Create a sorted copy of
nums1
callednums
for binary searching - Calculate the initial absolute sum difference:
s = sum(abs(a - b) for a, b in zip(nums1, nums2)) % mod
- Initialize
mx = 0
to track the maximum reduction we can achieve
Step 2: Find Optimal Replacement for Each Position
For each pair (a, b)
from (nums1[i], nums2[i])
:
-
Calculate the current difference:
d1 = abs(a - b)
-
Find the best replacement value from
nums1
that would minimize the difference withb
:- Use
bisect_left(nums, b)
to find the insertion positioni
whereb
would fit in the sorted array - Check two candidates for the closest value:
- If
i < len(nums)
: checknums[i]
(the element at or just afterb
) - If
i > 0
: checknums[i-1]
(the element just beforeb
)
- If
- Calculate
d2
as the minimum absolute difference using the best replacement
- Use
-
Update the maximum reduction:
mx = max(mx, d1 - d2)
d1 - d2
represents how much we can reduce the sum by replacinga
with the optimal value
Step 3: Calculate Final Answer
Return (s - mx + mod) % mod
- We subtract the maximum reduction from the original sum
- Add
mod
before taking modulo to handle potential negative values correctly
Time Complexity: O(n log n)
due to sorting and n
binary searches
Space Complexity: O(n)
for storing the sorted copy of nums1
The binary search is crucial here because it allows us to efficiently find the closest element in O(log n)
time for each position, rather than checking all elements which would take O(n²)
time overall.
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.
Given:
nums1 = [5, 10, 3]
nums2 = [7, 2, 8]
Step 1: Initial Setup
- Create sorted copy:
nums = [3, 5, 10]
- Calculate initial sum:
|5-7| + |10-2| + |3-8| = 2 + 8 + 5 = 15
- Initialize
mx = 0
(maximum reduction tracker)
Step 2: Find Optimal Replacement for Each Position
Position 0: (a=5, b=7)
- Current difference:
d1 = |5-7| = 2
- Find closest value to 7 in sorted
nums = [3, 5, 10]
:bisect_left([3, 5, 10], 7)
returns index 2- Check candidate at index 2:
nums[2] = 10
, difference =|10-7| = 3
- Check candidate at index 1:
nums[1] = 5
, difference =|5-7| = 2
- Best replacement gives
d2 = 2
- Reduction:
d1 - d2 = 2 - 2 = 0
- Update:
mx = max(0, 0) = 0
Position 1: (a=10, b=2)
- Current difference:
d1 = |10-2| = 8
- Find closest value to 2 in sorted
nums = [3, 5, 10]
:bisect_left([3, 5, 10], 2)
returns index 0- Check candidate at index 0:
nums[0] = 3
, difference =|3-2| = 1
- No candidate before index 0
- Best replacement gives
d2 = 1
- Reduction:
d1 - d2 = 8 - 1 = 7
- Update:
mx = max(0, 7) = 7
Position 2: (a=3, b=8)
- Current difference:
d1 = |3-8| = 5
- Find closest value to 8 in sorted
nums = [3, 5, 10]
:bisect_left([3, 5, 10], 8)
returns index 2- Check candidate at index 2:
nums[2] = 10
, difference =|10-8| = 2
- Check candidate at index 1:
nums[1] = 5
, difference =|5-8| = 3
- Best replacement gives
d2 = 2
- Reduction:
d1 - d2 = 5 - 2 = 3
- Update:
mx = max(7, 3) = 7
Step 3: Calculate Final Answer
- Original sum: 15
- Maximum reduction: 7
- Final answer:
15 - 7 = 8
The optimal replacement is at position 1, where we replace nums1[1] = 10
with 3
(from nums1[2]
), giving us the array [5, 3, 3]
and reducing the total sum from 15 to 8.
Solution Implementation
1class Solution:
2 def minAbsoluteSumDiff(self, nums1: List[int], nums2: List[int]) -> int:
3 # Define modulo constant for the result
4 MOD = 10**9 + 7
5
6 # Create a sorted copy of nums1 for binary search
7 sorted_nums1 = sorted(nums1)
8
9 # Calculate initial total absolute difference sum
10 total_diff = sum(abs(num1 - num2) for num1, num2 in zip(nums1, nums2)) % MOD
11
12 # Track maximum reduction possible by replacing one element
13 max_reduction = 0
14
15 # Try replacing each element in nums1 to find the best reduction
16 for num1, num2 in zip(nums1, nums2):
17 # Current difference for this pair
18 current_diff = abs(num1 - num2)
19
20 # Find the minimum possible difference if we replace num1
21 # with another value from nums1
22 min_possible_diff = float('inf')
23
24 # Use binary search to find the closest value to num2 in sorted_nums1
25 insertion_point = bisect_left(sorted_nums1, num2)
26
27 # Check the value at or after the insertion point
28 if insertion_point < len(sorted_nums1):
29 min_possible_diff = min(min_possible_diff, abs(sorted_nums1[insertion_point] - num2))
30
31 # Check the value before the insertion point
32 if insertion_point > 0:
33 min_possible_diff = min(min_possible_diff, abs(sorted_nums1[insertion_point - 1] - num2))
34
35 # Calculate reduction achieved by this replacement
36 reduction = current_diff - min_possible_diff
37 max_reduction = max(max_reduction, reduction)
38
39 # Return the minimized sum after applying the best reduction
40 return (total_diff - max_reduction + MOD) % MOD
41
1class Solution {
2 public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
3 // Define modulo constant for preventing integer overflow
4 final int MOD = (int) 1e9 + 7;
5
6 // Create a sorted copy of nums1 for binary search
7 int[] sortedNums1 = nums1.clone();
8 Arrays.sort(sortedNums1);
9
10 // Calculate the initial total sum of absolute differences
11 int totalSum = 0;
12 int n = nums1.length;
13 for (int i = 0; i < n; i++) {
14 totalSum = (totalSum + Math.abs(nums1[i] - nums2[i])) % MOD;
15 }
16
17 // Find the maximum reduction possible by replacing one element
18 int maxReduction = 0;
19 for (int i = 0; i < n; i++) {
20 // Calculate original difference at position i
21 int originalDiff = Math.abs(nums1[i] - nums2[i]);
22
23 // Initialize the minimum possible difference after replacement
24 int minPossibleDiff = Integer.MAX_VALUE;
25
26 // Find the position where nums2[i] would be inserted in sorted array
27 int insertPosition = binarySearch(sortedNums1, nums2[i]);
28
29 // Check the element at insert position (ceiling element)
30 if (insertPosition < n) {
31 minPossibleDiff = Math.min(minPossibleDiff,
32 Math.abs(sortedNums1[insertPosition] - nums2[i]));
33 }
34
35 // Check the element before insert position (floor element)
36 if (insertPosition > 0) {
37 minPossibleDiff = Math.min(minPossibleDiff,
38 Math.abs(sortedNums1[insertPosition - 1] - nums2[i]));
39 }
40
41 // Update maximum reduction (original difference - new minimum difference)
42 maxReduction = Math.max(maxReduction, originalDiff - minPossibleDiff);
43 }
44
45 // Return the minimum sum after applying the best replacement
46 // Add MOD to handle negative results before taking modulo
47 return (totalSum - maxReduction + MOD) % MOD;
48 }
49
50 /**
51 * Binary search to find the leftmost position where target can be inserted
52 * Returns the index of the first element >= target
53 * @param nums sorted array to search in
54 * @param target value to search for
55 * @return insertion position (0 to nums.length)
56 */
57 private int binarySearch(int[] nums, int target) {
58 int left = 0;
59 int right = nums.length;
60
61 while (left < right) {
62 int mid = (left + right) >>> 1; // Unsigned right shift to avoid overflow
63
64 if (nums[mid] >= target) {
65 right = mid; // Target might be at mid or to the left
66 } else {
67 left = mid + 1; // Target is definitely to the right
68 }
69 }
70
71 return left;
72 }
73}
74
1class Solution {
2public:
3 int minAbsoluteSumDiff(vector<int>& nums1, vector<int>& nums2) {
4 const int MOD = 1e9 + 7;
5
6 // Create a sorted copy of nums1 for binary search
7 vector<int> sortedNums1(nums1);
8 sort(sortedNums1.begin(), sortedNums1.end());
9
10 // Calculate the initial total sum of absolute differences
11 int totalSum = 0;
12 int n = nums1.size();
13 for (int i = 0; i < n; ++i) {
14 totalSum = (totalSum + abs(nums1[i] - nums2[i])) % MOD;
15 }
16
17 // Find the maximum reduction we can achieve by replacing one element
18 int maxReduction = 0;
19 for (int i = 0; i < n; ++i) {
20 // Current absolute difference at position i
21 int currentDiff = abs(nums1[i] - nums2[i]);
22
23 // Initialize minimum possible difference after replacement
24 int minPossibleDiff = INT_MAX;
25
26 // Find the closest value in nums1 to nums2[i] using binary search
27 int idx = lower_bound(sortedNums1.begin(), sortedNums1.end(), nums2[i]) - sortedNums1.begin();
28
29 // Check the element at or after nums2[i]
30 if (idx < n) {
31 minPossibleDiff = min(minPossibleDiff, abs(sortedNums1[idx] - nums2[i]));
32 }
33
34 // Check the element before nums2[i]
35 if (idx > 0) {
36 minPossibleDiff = min(minPossibleDiff, abs(sortedNums1[idx - 1] - nums2[i]));
37 }
38
39 // Calculate the reduction in sum if we replace nums1[i] with the best option
40 maxReduction = max(maxReduction, currentDiff - minPossibleDiff);
41 }
42
43 // Return the minimum absolute sum difference after optimal replacement
44 return (totalSum - maxReduction + MOD) % MOD;
45 }
46};
47
1/**
2 * Finds the minimum absolute sum of differences after replacing at most one element
3 * @param nums1 - First array of numbers
4 * @param nums2 - Second array of numbers
5 * @returns The minimum possible sum of absolute differences modulo 10^9 + 7
6 */
7function minAbsoluteSumDiff(nums1: number[], nums2: number[]): number {
8 const MOD = 10 ** 9 + 7;
9
10 // Create a sorted copy of nums1 for binary search
11 const sortedNums1 = [...nums1];
12 sortedNums1.sort((a, b) => a - b);
13
14 const arrayLength = nums1.length;
15
16 // Calculate the initial sum of absolute differences
17 let totalSum = 0;
18 for (let i = 0; i < arrayLength; ++i) {
19 totalSum = (totalSum + Math.abs(nums1[i] - nums2[i])) % MOD;
20 }
21
22 // Find the maximum reduction possible by replacing one element
23 let maxReduction = 0;
24
25 for (let i = 0; i < arrayLength; ++i) {
26 // Current absolute difference at position i
27 const originalDiff = Math.abs(nums1[i] - nums2[i]);
28
29 // Initialize minimum possible difference after replacement
30 let minPossibleDiff = 1 << 30; // Large initial value
31
32 // Find the position where nums2[i] would be inserted in sorted array
33 const insertPosition = search(sortedNums1, nums2[i]);
34
35 // Check the element at or after the insertion position
36 if (insertPosition < arrayLength) {
37 minPossibleDiff = Math.min(minPossibleDiff, Math.abs(sortedNums1[insertPosition] - nums2[i]));
38 }
39
40 // Check the element before the insertion position
41 if (insertPosition > 0) {
42 minPossibleDiff = Math.min(minPossibleDiff, Math.abs(sortedNums1[insertPosition - 1] - nums2[i]));
43 }
44
45 // Calculate the reduction achieved by this replacement
46 maxReduction = Math.max(maxReduction, originalDiff - minPossibleDiff);
47 }
48
49 // Return the minimum sum after applying the best replacement
50 return (totalSum - maxReduction + MOD) % MOD;
51}
52
53/**
54 * Binary search to find the leftmost position where target can be inserted
55 * @param nums - Sorted array of numbers
56 * @param target - Target value to search for
57 * @returns The index where target should be inserted to maintain sorted order
58 */
59function search(nums: number[], target: number): number {
60 let left = 0;
61 let right = nums.length;
62
63 // Binary search for lower bound
64 while (left < right) {
65 const mid = (left + right) >> 1; // Equivalent to Math.floor((left + right) / 2)
66
67 if (nums[mid] >= target) {
68 right = mid;
69 } else {
70 left = mid + 1;
71 }
72 }
73
74 return left;
75}
76
Time and Space Complexity
The time complexity is O(n × log n)
, where n
is the length of the array nums1
.
- Sorting
nums1
takesO(n × log n)
time - Computing the initial sum takes
O(n)
time - The main loop iterates
n
times, and for each iteration:bisect_left
performs binary search on the sorted array, takingO(log n)
time- All other operations inside the loop take
O(1)
time
- Therefore, the loop contributes
O(n × log n)
time - Overall time complexity:
O(n × log n) + O(n) + O(n × log n) = O(n × log n)
The space complexity is O(n)
.
- Creating a sorted copy of
nums1
requiresO(n)
additional space - All other variables (
s
,mx
,d1
,d2
,i
) useO(1)
space - Overall space complexity:
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Modulo Operation Handling Error
Pitfall: A common mistake is incorrectly handling the modulo operation when calculating the final result. Simply doing (total_diff - max_reduction) % MOD
can produce incorrect results when max_reduction > total_diff
.
Why it happens: When we subtract max_reduction
from total_diff
, we might get a negative number. In Python, the modulo of a negative number can produce unexpected results in the context of this problem.
Example scenario:
- If
total_diff = 5
andmax_reduction = 7
- Direct calculation:
(5 - 7) % MOD = -2 % MOD = 1000000005
(in Python) - But we want the result to be equivalent to a positive difference
Solution: Add MOD
before taking the modulo to ensure a positive result:
return (total_diff - max_reduction + MOD) % MOD
2. Integer Overflow in Initial Sum Calculation
Pitfall: Forgetting to apply modulo during the initial sum calculation, which could lead to integer overflow in languages with fixed integer sizes (though Python handles big integers automatically).
Why it happens: The sum of absolute differences across all elements could exceed the modulo value significantly before we apply the modulo operation.
Solution: Apply modulo immediately after calculating the initial sum:
total_diff = sum(abs(num1 - num2) for num1, num2 in zip(nums1, nums2)) % MOD
3. Not Checking Both Neighbors in Binary Search
Pitfall: Only checking one candidate value after binary search, missing the optimal replacement.
Why it happens: bisect_left
returns the leftmost insertion position. The closest value to num2
could be either at this position or at the position just before it.
Example:
sorted_nums1 = [1, 5, 10]
andnum2 = 6
bisect_left
returns index 2 (position for inserting 6)- Only checking index 2 gives us
10
with difference|10-6| = 4
- But checking index 1 gives us
5
with difference|5-6| = 1
(better!)
Solution: Always check both candidates when they exist:
if insertion_point < len(sorted_nums1):
min_possible_diff = min(min_possible_diff, abs(sorted_nums1[insertion_point] - num2))
if insertion_point > 0:
min_possible_diff = min(min_possible_diff, abs(sorted_nums1[insertion_point - 1] - num2))
4. Forgetting Edge Cases with Empty or Single Element Arrays
Pitfall: Not handling cases where the insertion point is at the boundaries (0 or len(array)).
Solution: The code correctly uses conditional checks (if insertion_point < len(sorted_nums1)
and if insertion_point > 0
) to avoid index out of bounds errors.
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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!