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 Implementation

1class Solution:
2    def minAbsoluteSumDiff(self, nums1: List[int], nums2: List[int]) -> int:
3        MOD = 10**9 + 7
4
5        # Create a sorted copy of nums1 for binary search
6        sorted_nums1 = sorted(nums1)
7        n = len(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        def find_first_ge(target: int) -> int:
13            """Find the first index where sorted_nums1[index] >= target using template."""
14            left, right = 0, n - 1
15            first_true_index = -1
16
17            while left <= right:
18                mid = (left + right) // 2
19                if sorted_nums1[mid] >= target:
20                    first_true_index = mid
21                    right = mid - 1
22                else:
23                    left = mid + 1
24
25            return first_true_index
26
27        # Track maximum reduction possible by replacing one element
28        max_reduction = 0
29
30        for num1, num2 in zip(nums1, nums2):
31            current_diff = abs(num1 - num2)
32            min_possible_diff = float('inf')
33
34            # Find first element >= num2
35            idx = find_first_ge(num2)
36
37            # Check the value at or after num2
38            if idx != -1:
39                min_possible_diff = min(min_possible_diff, abs(sorted_nums1[idx] - num2))
40
41            # Check the value before num2
42            if idx == -1:
43                # All elements are < num2, check the last one
44                min_possible_diff = min(min_possible_diff, abs(sorted_nums1[n - 1] - num2))
45            elif idx > 0:
46                min_possible_diff = min(min_possible_diff, abs(sorted_nums1[idx - 1] - num2))
47
48            reduction = current_diff - min_possible_diff
49            max_reduction = max(max_reduction, reduction)
50
51        return (total_diff - max_reduction + MOD) % MOD
52
1class Solution {
2    public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
3        final int MOD = (int) 1e9 + 7;
4
5        int[] sortedNums1 = nums1.clone();
6        Arrays.sort(sortedNums1);
7        int n = nums1.length;
8
9        int totalSum = 0;
10        for (int i = 0; i < n; i++) {
11            totalSum = (totalSum + Math.abs(nums1[i] - nums2[i])) % MOD;
12        }
13
14        int maxReduction = 0;
15        for (int i = 0; i < n; i++) {
16            int originalDiff = Math.abs(nums1[i] - nums2[i]);
17            int minPossibleDiff = Integer.MAX_VALUE;
18
19            // Find first index where sortedNums1[index] >= nums2[i]
20            int idx = findFirstGe(sortedNums1, nums2[i]);
21
22            // Check the element at or after nums2[i]
23            if (idx != -1) {
24                minPossibleDiff = Math.min(minPossibleDiff, Math.abs(sortedNums1[idx] - nums2[i]));
25            }
26
27            // Check the element before nums2[i]
28            if (idx == -1) {
29                minPossibleDiff = Math.min(minPossibleDiff, Math.abs(sortedNums1[n - 1] - nums2[i]));
30            } else if (idx > 0) {
31                minPossibleDiff = Math.min(minPossibleDiff, Math.abs(sortedNums1[idx - 1] - nums2[i]));
32            }
33
34            maxReduction = Math.max(maxReduction, originalDiff - minPossibleDiff);
35        }
36
37        return (totalSum - maxReduction + MOD) % MOD;
38    }
39
40    private int findFirstGe(int[] nums, int target) {
41        int left = 0;
42        int right = nums.length - 1;
43        int firstTrueIndex = -1;
44
45        while (left <= right) {
46            int mid = left + (right - left) / 2;
47            if (nums[mid] >= target) {
48                firstTrueIndex = mid;
49                right = mid - 1;
50            } else {
51                left = mid + 1;
52            }
53        }
54
55        return firstTrueIndex;
56    }
57}
58
1class Solution {
2public:
3    int minAbsoluteSumDiff(vector<int>& nums1, vector<int>& nums2) {
4        const int MOD = 1e9 + 7;
5
6        vector<int> sortedNums1(nums1);
7        sort(sortedNums1.begin(), sortedNums1.end());
8        int n = nums1.size();
9
10        int totalSum = 0;
11        for (int i = 0; i < n; ++i) {
12            totalSum = (totalSum + abs(nums1[i] - nums2[i])) % MOD;
13        }
14
15        auto findFirstGe = [&](int target) -> int {
16            int left = 0;
17            int right = n - 1;
18            int firstTrueIndex = -1;
19
20            while (left <= right) {
21                int mid = left + (right - left) / 2;
22                if (sortedNums1[mid] >= target) {
23                    firstTrueIndex = mid;
24                    right = mid - 1;
25                } else {
26                    left = mid + 1;
27                }
28            }
29
30            return firstTrueIndex;
31        };
32
33        int maxReduction = 0;
34        for (int i = 0; i < n; ++i) {
35            int currentDiff = abs(nums1[i] - nums2[i]);
36            int minPossibleDiff = INT_MAX;
37
38            int idx = findFirstGe(nums2[i]);
39
40            if (idx != -1) {
41                minPossibleDiff = min(minPossibleDiff, abs(sortedNums1[idx] - nums2[i]));
42            }
43
44            if (idx == -1) {
45                minPossibleDiff = min(minPossibleDiff, abs(sortedNums1[n - 1] - nums2[i]));
46            } else if (idx > 0) {
47                minPossibleDiff = min(minPossibleDiff, abs(sortedNums1[idx - 1] - nums2[i]));
48            }
49
50            maxReduction = max(maxReduction, currentDiff - minPossibleDiff);
51        }
52
53        return (totalSum - maxReduction + MOD) % MOD;
54    }
55};
56
1function minAbsoluteSumDiff(nums1: number[], nums2: number[]): number {
2    const MOD = 10 ** 9 + 7;
3
4    const sortedNums1 = [...nums1];
5    sortedNums1.sort((a, b) => a - b);
6    const n = nums1.length;
7
8    let totalSum = 0;
9    for (let i = 0; i < n; ++i) {
10        totalSum = (totalSum + Math.abs(nums1[i] - nums2[i])) % MOD;
11    }
12
13    const findFirstGe = (target: number): number => {
14        let left = 0;
15        let right = n - 1;
16        let firstTrueIndex = -1;
17
18        while (left <= right) {
19            const mid = Math.floor((left + right) / 2);
20            if (sortedNums1[mid] >= target) {
21                firstTrueIndex = mid;
22                right = mid - 1;
23            } else {
24                left = mid + 1;
25            }
26        }
27
28        return firstTrueIndex;
29    };
30
31    let maxReduction = 0;
32
33    for (let i = 0; i < n; ++i) {
34        const originalDiff = Math.abs(nums1[i] - nums2[i]);
35        let minPossibleDiff = 1 << 30;
36
37        const idx = findFirstGe(nums2[i]);
38
39        if (idx !== -1) {
40            minPossibleDiff = Math.min(minPossibleDiff, Math.abs(sortedNums1[idx] - nums2[i]));
41        }
42
43        if (idx === -1) {
44            minPossibleDiff = Math.min(minPossibleDiff, Math.abs(sortedNums1[n - 1] - nums2[i]));
45        } else if (idx > 0) {
46            minPossibleDiff = Math.min(minPossibleDiff, Math.abs(sortedNums1[idx - 1] - nums2[i]));
47        }
48
49        maxReduction = Math.max(maxReduction, originalDiff - minPossibleDiff);
50    }
51
52    return (totalSum - maxReduction + MOD) % MOD;
53}
54

Solution Approach

The solution uses sorting and the binary search template to find the optimal replacement.

Step 1: Initial Setup

  • Create a sorted copy of nums1 for binary searching
  • Calculate the initial absolute sum difference

Step 2: Binary Search Template for Finding Closest Element

We use the template to find the first element >= target:

def find_first_ge(target: int) -> int:
    left, right = 0, n - 1
    first_true_index = -1

    while left <= right:
        mid = (left + right) // 2
        if sorted_nums1[mid] >= target:
            first_true_index = mid
            right = mid - 1
        else:
            left = mid + 1

    return first_true_index

The feasibility condition sorted_nums1[mid] >= target creates the pattern:

  • Elements before target: return false
  • Elements at or after target: return true

Step 3: Find Optimal Replacement for Each Position

For each pair (num1, num2):

  1. Calculate the current difference: current_diff = abs(num1 - num2)
  2. Use find_first_ge(num2) to find the first element >= num2
  3. Check two candidates for the closest value:
    • If idx != -1: check sorted_nums1[idx] (first element >= num2)
    • If idx == -1: all elements < num2, check the last element
    • If idx > 0: check sorted_nums1[idx-1] (element just before)
  4. Track the maximum possible reduction

Step 4: Calculate Final Answer

Return (total_diff - max_reduction + MOD) % MOD

Time Complexity: O(n log n) for sorting and n binary searches Space Complexity: O(n) for storing the sorted copy of nums1

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.

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. Using Wrong Binary Search Template Variant

Pitfall: Using while left < right with right = mid instead of the standard template.

Wrong Implementation:

while left < right:
    mid = (left + right) >> 1
    if nums[mid] >= target:
        right = mid
    else:
        left = mid + 1
return left

Correct Implementation (using template):

left, right = 0, n - 1
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if sorted_nums1[mid] >= target:
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1

return first_true_index

2. Modulo Operation Handling Error

Pitfall: Simply doing (total_diff - max_reduction) % MOD can produce issues.

Solution: Add MOD before taking the modulo:

return (total_diff - max_reduction + MOD) % MOD

3. Not Checking Both Neighbors in Binary Search

Pitfall: Only checking one candidate value after binary search, missing the optimal replacement.

Example:

  • sorted_nums1 = [1, 5, 10] and num2 = 6
  • Template returns first_true_index = 2 (element 10 >= 6)
  • Only checking index 2 gives difference 4
  • But checking index 1 gives difference 1 (better!)

Solution: Check both first_true_index and first_true_index - 1 when they exist.

4. Handling first_true_index = -1

Pitfall: Not handling the case when all elements are less than the target.

Solution: When first_true_index == -1, the closest element must be the last one:

if idx == -1:
    min_possible_diff = min(min_possible_diff, abs(sorted_nums1[n - 1] - num2))
Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which two pointer techniques do you use to check if a string is a palindrome?


Recommended Readings

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

Load More