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:

  1. Calculate the initial absolute sum difference without any replacements.
  2. Sort a copy of nums1 to prepare for binary search.
  3. Iterate through each element in nums2, and for each, search for the closest element in the sorted nums1 using binary search.
  4. Determine the absolute difference reduction that could be achieved by replacing nums1[i] with this closest number.
  5. Record the maximum amount by which we could reduce a single absolute difference.
  6. 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 from O(n) to O(log n) for searching.

  • Binary Search: For each element b in nums2, we use binary search to find the position in the sorted nums1 where b could be inserted while maintaining the sort order. This gives us the element just larger than or equal to b as well as the one just smaller than b if one exists. These are the potential candidates for minimizing the difference with b if we were to make a replacement in nums1.

  • 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:

  1. Initialize mod = 10**9 + 7 to hold the value for modular arithmetic.
  2. Calculate the initial sum of absolute differences s and mod it with mod.
  3. Store the maximum reduction found as mx. Initially, mx is 0.
  4. Iterate over the elements of nums1 and nums2.
    • For each pair (a, b), calculate the current difference d1.
    • Perform a binary search in the sorted nums to locate the insertion point for b.
    • 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.
  5. Finally, subtract this maximum achievable difference reduction mx from the initial sum s, 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 Evaluator

Example 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 is 3, the closest in nums is 1 (binary search would yield index 1 since 3 can be inserted before 5).
  • For the second element 9, the closest is 7 (insertion index would be at the end of nums).
  • For the third element 2, 1 is the closest in nums (binary search gives index 1 because 2 can be inserted before 5).

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:

  1. Sorting nums1 takes O(n log n) time.
  2. The list comprehension calculating the sum of absolute differences runs in O(n).
  3. The for loop with binary search (bisect_left) inside runs in O(n log n) time, because bisect_left runs in O(log n) and is called up to twice for each of the n elements in nums1.

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:

  1. The sorted list nums, which takes O(n) space.
  2. The variables mod, s, and mx, which together use a constant amount of space O(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.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

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

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


Load More