Facebook Pixel

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 with nums1[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 if x >= 0
  • -x if x < 0
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. First, calculate the total absolute sum difference without any replacements
  2. For each position i, find the element in nums1 that is closest to nums2[i]
  3. Calculate how much we can reduce the sum by making this replacement
  4. Track the maximum possible reduction across all positions
  5. 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 called nums 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]):

  1. Calculate the current difference: d1 = abs(a - b)

  2. Find the best replacement value from nums1 that would minimize the difference with b:

    • Use bisect_left(nums, b) to find the insertion position i where b would fit in the sorted array
    • Check two candidates for the closest value:
      • If i < len(nums): check nums[i] (the element at or just after b)
      • If i > 0: check nums[i-1] (the element just before b)
    • Calculate d2 as the minimum absolute difference using the best replacement
  3. Update the maximum reduction: mx = max(mx, d1 - d2)

    • d1 - d2 represents how much we can reduce the sum by replacing a 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 Evaluator

Example 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 takes O(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, taking O(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 requires O(n) additional space
  • All other variables (s, mx, d1, d2, i) use O(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 and max_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] and num2 = 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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

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

Load More