1818. Minimum Absolute Sum Difference
Problem Description
In this problem, you are provided with two arrays of positive integers nums1
and nums2
, both having the same length n
. You need to calculate the absolute sum difference between the two arrays, which is the sum of the absolute differences between each pair of elements |nums1[i] - nums2[i]|
at each index i
ranging from 0 to n-1
.
However, there's a twist. You have the option to replace one element from nums1
with any other element from the same nums1
array to minimize the overall absolute sum difference. After performing this optional replacement, you should return the minimum possible absolute sum difference. To account for potentially large numbers, the result should be presented modulo 10^9 + 7
.
The goal is to find out how one can smartly replace a single element in nums1
to achieve the smallest possible absolute sum difference between nums1
and nums2
.
Intuition
To approach this problem, one natural thought could be to check every possible single replacement in nums1
to minimize the absolute sum difference — but this would be inefficient for large arrays. To avoid an overly complicated and slow solution, we can use binary search, which is much more efficient.
The intuition here is based on finding, for each element nums2[i]
, the closest element in the sorted nums1
. Since a sorted list is provided, binary search can be used to find this closest element efficiently.
Here's a step-by-step on how we achieve this:
- Calculate the initial absolute sum difference without any replacements.
- Sort a copy of
nums1
to prepare for binary search. - Iterate through each element in
nums2
, and for each, search for the closest element in the sortednums1
using binary search. - Determine the absolute difference reduction that could be achieved by replacing
nums1[i]
with this closest number. - Record the maximum amount by which we could reduce a single absolute difference.
- Subtract this maximum reduction from the total absolute sum difference calculated in step 1 to achieve the minimum absolute sum difference after the single replacement.
Using binary search optimizes this process, because scanning through the sorted array for the nearest number would otherwise be a linear operation, which, when nested in another linear operation, would result in quadratic time complexity. Binary search turns the inner linear scan into a logarithmic one, thus significantly enhancing performance.
Learn more about Binary Search and Sorting patterns.
Solution Approach
The solution employs binary search, sorting, and modular arithmetic as its primary techniques:
-
Sorting: A sorted version of
nums1
is created, which allows for the efficient use of binary search. Sorting places elements in a sequence that binary search can exploit to reduce the time complexity fromO(n)
toO(log n)
for searching. -
Binary Search: For each element
b
innums2
, we use binary search to find the position in the sortednums1
whereb
could be inserted while maintaining the sort order. This gives us the element just larger than or equal tob
as well as the one just smaller thanb
if one exists. These are the potential candidates for minimizing the difference withb
if we were to make a replacement innums1
. -
Modular Arithmetic: Due to the constraints regarding large numbers, every sum is computed modulo
10^9 + 7
, which is a prime number often used to avoid overflow issues in competitive programming.
Here's how the implementation reflects the algorithm:
- Initialize
mod = 10**9 + 7
to hold the value for modular arithmetic. - Calculate the initial sum of absolute differences
s
and mod it withmod
. - Store the maximum reduction found as
mx
. Initially,mx
is 0. - Iterate over the elements of
nums1
andnums2
.- For each pair
(a, b)
, calculate the current differenced1
. - Perform a binary search in the sorted
nums
to locate the insertion point forb
. - If the insertion point is not at the beginning, calculate a potential reduced difference with the element just before the insertion point and update
d2
if it's smaller. - If the insertion point is not at the end, also calculate the potential reduced difference with the element at the insertion point and update
d2
if it's smaller. - Update
mx
to be the maximum difference that can be reduced for any element.
- For each pair
- Finally, subtract this maximum achievable difference reduction
mx
from the initial sums
, and apply modular arithmetic to obtain the final result.
class Solution:
def minAbsoluteSumDiff(self, nums1: List[int], nums2: List[int]) -> int:
mod = 10**9 + 7
# Create a sorted copy of nums1 for [binary search](/problems/binary-search-speedrun).
nums = sorted(nums1)
# Calculate the initial sum of absolute differences.
s = sum(abs(a - b) for a, b in zip(nums1, nums2)) % mod
# Initialize the variable to track the maximum reduction possible.
mx = 0
# Iterate over each element of nums1 and nums2.
for a, b in zip(nums1, nums2):
# Calculate the current difference `d1`.
d1 = abs(a - b)
# Using binary search, find the position `i` to insert `b` in sorted `nums1`.
i = bisect_left(nums, b)
d2 = float('inf') # Initialize a variable for the potential new difference.
# Check the element at the insertion index if not at the end of the array.
if i < len(nums):
d2 = abs(nums[i] - b)
# Check the element before the insertion index if possible to find a better candidate.
if i:
d2 = min(d2, abs(nums[i - 1] - b))
# Update `mx` if we found a better reduction.
mx = max(mx, d1 - d2)
# The result is the initial sum `s` reduced by `mx` and then modded by `mod`.
return (s - mx) % mod
By applying these techniques, the provided solution ensures that we find the minimum absolute sum difference in an optimized manner and handle potential overflow issues related to large sums effectively.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's take the following small example to illustrate the solution approach:
nums1 = [1, 7, 5] nums2 = [3, 9, 2]
We are to compute the absolute sum difference between these two arrays, while minimizing this value by replacing up to one element in nums1
.
Step 1: Calculate the Initial Absolute Sum Difference
We first calculate the sum of absolute differences without any replacements:
|1 - 3| + |7 - 9| + |5 - 2| = 2 + 2 + 3 = 7
Step 2: Sort a Copy of nums1
for Binary Search
We then sort nums1
to:
nums = [1, 5, 7]
Step 3: Iterate and Binary Search for Each Element in nums2
Next, for each element in nums2
, we look for the closest element in the sorted nums
:
- For the first element in
nums2
which is3
, the closest innums
is1
(binary search would yield index1
since3
can be inserted before5
). - For the second element
9
, the closest is7
(insertion index would be at the end ofnums
). - For the third element
2
,1
is the closest innums
(binary search gives index1
because2
can be inserted before5
).
Step 4: Determine Absolute Difference Reduction
We now find the absolute difference reductions for each element in nums2
:
- Reduction with
3
could be|1 - 3| = 2
to|5 - 3| = 2
(no reduction). - Reduction with
9
could be|7 - 9| = 2
to|7 - 9| = 2
(no reduction). - The most significant potential reduction comes from the element
2
, where|5 - 2| = 3
can be reduced to|1 - 2| = 1
. Here, a reduction of 2 is possible.
Step 5: Record the Maximum Absolute Difference Reduction
In this case, the reduction is 2 for the last element.
Step 6: Apply the Maximum Reduction to the Initial Sum
The initial sum was 7
. After reducing the maximum reducible difference (2
), we get 7 - 2 = 5
.
Therefore, the minimum possible absolute sum difference after performing the optional replacement is 5
, with no need for modular arithmetic since our numbers are small. In practice, we would take the result modulo 10^9 + 7
to ensure it fits within integer limits for larger sums.
Solution Implementation
1from bisect import bisect_left
2
3class Solution:
4 def minAbsoluteSumDiff(self, nums1: List[int], nums2: List[int]) -> int:
5 # Define the modulo for the result
6 modulo = 10**9 + 7
7
8 # Create a sorted copy of nums1 for binary search
9 sorted_nums1 = sorted(nums1)
10
11 # Calculate the initial sum of absolute differences
12 sum_diff = sum(abs(a - b) for a, b in zip(nums1, nums2)) % modulo
13
14 # Initialize the variable to keep track of the maximum reduction we can achieve
15 max_reduction = 0
16
17 # Iterate over the pairs from nums1 and nums2
18 for num1, num2 in zip(nums1, nums2):
19 # The current absolute difference
20 current_diff = abs(num1 - num2)
21
22 # Use binary search to find the closest number in sorted_nums1 to num2
23 index = bisect_left(sorted_nums1, num2)
24 # Initialize a variable to store the minimum possible difference after replacement
25 min_possible_diff = float('inf')
26
27 # Check if there is an element at or after the index in the sorted list
28 if index < len(sorted_nums1):
29 min_possible_diff = min(min_possible_diff, abs(sorted_nums1[index] - num2))
30 # Check if there is an element before the index in the sorted list
31 if index > 0:
32 min_possible_diff = min(min_possible_diff, abs(sorted_nums1[index - 1] - num2))
33
34 # Calculate the maximum reduction by comparing the current maximum and the new difference
35 max_reduction = max(max_reduction, current_diff - min_possible_diff)
36
37 # Adjust the initial sum by the maximum reduction found, then modulo the result
38 result = (sum_diff - max_reduction + modulo) % modulo
39
40 # Return the result
41 return result
42
1class Solution {
2 public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
3 // The modulo constant to avoid overflow issues
4 final int MOD = (int) 1e9 + 7;
5
6 // Clone and sort nums1 to use for binary search
7 int[] sortedNums = nums1.clone();
8 Arrays.sort(sortedNums);
9
10 // Calculate the initial sum of absolute differences
11 int sum = 0, length = nums1.length;
12 for (int i = 0; i < length; ++i) {
13 sum = (sum + Math.abs(nums1[i] - nums2[i])) % MOD;
14 }
15
16 // Variable to store the maximum possible reduction in the absolute sum difference
17 int maxReduction = 0;
18
19 // Find maximum reduction to minimize the absolute sum diff.
20 for (int i = 0; i < length; ++i) {
21 int currentDiff = Math.abs(nums1[i] - nums2[i]);
22 int closestNumDiff = Integer.MAX_VALUE;
23
24 // Search for the best number in sortedNums that minimizes the difference with nums2[i]
25 int idx = binarySearch(sortedNums, nums2[i]);
26 if (idx < length) {
27 closestNumDiff = Math.min(closestNumDiff, Math.abs(sortedNums[idx] - nums2[i]));
28 }
29 if (idx > 0) {
30 closestNumDiff = Math.min(closestNumDiff, Math.abs(sortedNums[idx - 1] - nums2[i]));
31 }
32
33 // Update max reduction using the new closest number
34 maxReduction = Math.max(maxReduction, currentDiff - closestNumDiff);
35 }
36
37 // Apply the max reduction found to minimize the sum and return the result modulo MOD
38 return (sum - maxReduction + MOD) % MOD;
39 }
40
41 // Helper method to perform binary search on the sorted array
42 private int binarySearch(int[] nums, int target) {
43 int left = 0, right = nums.length;
44 while (left < right) {
45 int mid = (left + right) >>> 1; // Use unsigned right shift for mid to avoid overflow
46 if (nums[mid] >= target) {
47 right = mid;
48 } else {
49 left = mid + 1;
50 }
51 }
52 return left;
53 }
54}
55
1class Solution {
2public:
3 int minAbsoluteSumDiff(vector<int>& nums1, vector<int>& nums2) {
4 // Defining the modulus for the result as per the problem statement
5 const int MOD = 1e9 + 7;
6
7 // Copy nums1 into a new vector and sort it to perform binary search later
8 vector<int> sortedNums(nums1);
9 sort(sortedNums.begin(), sortedNums.end());
10
11 // Calculate the initial sum of absolute differences between nums1 and nums2
12 int sumOfDifferences = 0;
13 int size = nums1.size();
14 for (int i = 0; i < size; ++i) {
15 sumOfDifferences = (sumOfDifferences + abs(nums1[i] - nums2[i])) % MOD;
16 }
17
18 // Initialize the variable to keep track of the maximum improvement
19 int maxImprovement = 0;
20
21 // Iterate through all the elements in nums1 and nums2
22 for (int i = 0; i < size; ++i) {
23 // Current absolute difference without any replacements
24 int currentDiff = abs(nums1[i] - nums2[i]);
25
26 // We initialize the improved difference as a large value
27 int improvedDiff = INT_MAX;
28
29 // Binary search to find the closest element in sortedNums to nums2[i]
30 auto lowerIt = lower_bound(sortedNums.begin(), sortedNums.end(), nums2[i]);
31
32 // If we found an element not at the end, check the improvement
33 if (lowerIt != sortedNums.end()) {
34 improvedDiff = min(improvedDiff, abs(*lowerIt - nums2[i]));
35 }
36
37 // If the iterator is not at the beginning, check previous element for potential improvement
38 if (lowerIt != sortedNums.begin()) {
39 improvedDiff = min(improvedDiff, abs(*prev(lowerIt) - nums2[i]));
40 }
41
42 // Calculate the improvement if this element is replaced
43 maxImprovement = max(maxImprovement, currentDiff - improvedDiff);
44 }
45
46 // Subtract the max improvement from the initial sum and return the result within the modulus
47 return (sumOfDifferences - maxImprovement + MOD) % MOD;
48 }
49};
50
1function minAbsoluteSumDiff(nums1: number[], nums2: number[]): number {
2 // Modulus value for handling big integers ensuring the result is within integer limits
3 const MOD = 10 ** 9 + 7;
4
5 // Clone nums1 and sort it to use it for efficient lookup
6 const sortedNums1 = [...nums1];
7 sortedNums1.sort((a, b) => a - b);
8
9 const n = nums1.length; // Size of input arrays
10 let sumOfDifferences = 0; // To store the sum of absolute differences
11
12 // Calculate the initial sum of differences
13 for (let i = 0; i < n; ++i) {
14 sumOfDifferences = (sumOfDifferences + Math.abs(nums1[i] - nums2[i])) % MOD;
15 }
16
17 // Max improvement in the sum by replacing an element in nums1
18 let maxImprovement = 0;
19
20 // Iterate through all elements to find max possible improvement
21 for (let i = 0; i < n; ++i) {
22 const currentDiff = Math.abs(nums1[i] - nums2[i]);
23 let minPotentialDiff = Infinity; // Initialize with a high value
24
25 // Find the closest number in sortedNums1 to nums2[i]
26 let binarySearchIndex = binarySearch(sortedNums1, nums2[i]);
27
28 // Compare with the found number and the number before it if possible
29 if (binarySearchIndex < n) {
30 minPotentialDiff = Math.min(minPotentialDiff, Math.abs(sortedNums1[binarySearchIndex] - nums2[i]));
31 }
32 if (binarySearchIndex > 0) {
33 minPotentialDiff = Math.min(minPotentialDiff, Math.abs(sortedNums1[binarySearchIndex - 1] - nums2[i]));
34 }
35
36 // Calculate improvement and update maxImprovement if it's better
37 maxImprovement = Math.max(maxImprovement, currentDiff - minPotentialDiff);
38 }
39
40 // Result is the initial sum minus the max improvement within the mod range
41 return (sumOfDifferences - maxImprovement + MOD) % MOD;
42}
43
44// Helper method to perform binary search
45function binarySearch(nums: number[], target: number): number {
46 let left = 0;
47 let right = nums.length;
48
49 // Standard binary search algorithm to find the left-most position where target can be placed
50 while (left < right) {
51 const mid = (left + right) >> 1; // Use bit shift to find mid avoiding overflow
52 if (nums[mid] >= target) {
53 right = mid;
54 } else {
55 left = mid + 1;
56 }
57 }
58
59 // Return the left index which is the insertion point of target
60 return left;
61}
62
Time and Space Complexity
Time Complexity
The time complexity of the provided code consists of several components:
- Sorting
nums1
takesO(n log n)
time. - The list comprehension calculating the sum of absolute differences runs in
O(n)
. - The for loop with binary search (
bisect_left
) inside runs inO(n log n)
time, becausebisect_left
runs inO(log n)
and is called up to twice for each of then
elements innums1
.
Thus, the overall time complexity is O(n log n + n + n log n)
, which simplifies to O(n log n)
.
Space Complexity
The space complexity of the code depends on the additional space required by:
- The sorted list
nums
, which takesO(n)
space. - The variables
mod
,s
, andmx
, which together use a constant amount of spaceO(1)
.
Consequently, the overall space complexity of the code is O(n)
(due to the space needed for the sorted list).
Learn more about how to find time and space complexity quickly using problem constraints.
Consider the classic dynamic programming of longest increasing subsequence:
Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
For example, the length of LIS for [50, 3, 10, 7, 40, 80]
is 4
and LIS is
[3, 7, 40, 80]
.
What is the recurrence relation?
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
LeetCode 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 we
Want a Structured Path to Master System Design Too? Don’t Miss This!