Problem overview
The method minimumAbsDifference receives an array of distinct integers, nums. It must find the smallest difference between any two elements and return every pair [a, b] that reaches it, with a < b. The pairs must be listed in ascending order.
In the first example, [4, 2, 1, 3], the closest values differ by 1, and three pairs do it: [1, 2], [2, 3] and [3, 4]. In the second example, [1, 3, 6, 10, 15], the smallest gap is 2 and only [1, 3] has it. In the third, [3, 8, -10, 23, 19, -4, -14, 27], the smallest gap is 4, shared by [-14, -10], [19, 23] and [23, 27].
The array has between 2 and 100,000 elements, each between -1,000,000 and 1,000,000. Checking every pair would take about n² comparisons, which is too many for 100,000 elements.
Approach: sort, then look only at neighbours
After sorting in ascending order, the closest pair is always made of two elements that are next to each other. Suppose two elements are further apart in the sorted array, with some value between them. That middle value forms a pair with each of them, and both gaps are smaller than the whole distance. So no pair with elements in between can be the closest.
That reduces the work to the n - 1 adjacent pairs. Walk through them once, track the smallest gap seen so far, and keep the list of pairs that have that gap. When a smaller gap appears, throw the list away and start a new one; when an equal gap appears, add to the list. A bonus of sorting is that the pairs are discovered in ascending order, as the statement requires.
For the first example, the sorted array is [1, 2, 3, 4]. The first gap is 1, so minDiff is 1 and the list starts empty. Index 1 gives gap 1, which equals minDiff, so [1, 2] is added. Index 2 adds [2, 3] and index 3 adds [3, 4].
Step by step
- Sort
numswith the comparator(a, b) => a - b. Without it, numbers are compared as strings, and the neighbours in that order are not the closest numbers. - Start
minDiffwith the first gap,nums[1] - nums[0], and createminDiffPairsas an emptynumber[][]. Both useletbecause they are reassigned. - Loop over the adjacent pairs, with
istarting at 1. - Compute the current gap as
const curDiff = nums[i] - nums[i - 1]. - If
curDiff < minDiff, this gap is a new record. - Replace the record. Set
minDifftocurDiffand makeminDiffPairsa new array holding only[nums[i - 1], nums[i]]. - Otherwise, if
curDiff === minDiff, the gap ties the record. - Add the tie: push
[nums[i - 1], nums[i]]ontominDiffPairs. - Return
minDiffPairsafter the loop.
The loop starts at index 1, so the very first gap is looked at again. It is equal to minDiff, so it is pushed on that pass. That's why the list can begin empty.
The solution in TypeScript
class Solution {
minimumAbsDifference(nums: number[]): number[][] {
nums.sort((a, b) => a - b);
let minDiff = nums[1] - nums[0];
let minDiffPairs: number[][] = [];
for (let i = 1; i < nums.length; i++) {
const curDiff = nums[i] - nums[i - 1];
if (curDiff < minDiff) {
minDiff = curDiff;
minDiffPairs = [[nums[i - 1], nums[i]]];
} else if (curDiff === minDiff) {
minDiffPairs.push([nums[i - 1], nums[i]]);
}
}
return minDiffPairs;
}
}
Details worth knowing:
- A nested array literal.
[[nums[i - 1], nums[i]]]is an array holding one pair. The outer brackets are the list, the inner ones are the pair, and the declared typenumber[][]matches. - Replace, don't clear. Assigning a fresh array to
minDiffPairsis safe here because nobody else holds the old list. constinside a loop. Each pass makes a newcurDiff, soconstis fine.- The comparator is required. The default
sort()would turn[10, 9, 100, 11]into[10, 100, 11, 9]. - The sort changes the caller's array, and returns pairs as new arrays.
Complexity
Time: O(n log n). The sort takes O(n log n), and the loop is a single pass.
Space: O(1) besides the output. Only a few variables are used. The returned list of pairs can hold up to n - 1 pairs, which is the answer itself. When the sort runs in place, no copy of the input is made.
Edge cases and common mistakes
- Two elements.
[5, 1]gives[[1, 5]]. The smaller value comes first in the pair. - Every gap equal.
[1, 2, 3, 4]returns all three adjacent pairs. - Negative values. Differences of sorted neighbours are never negative, so the sign of the numbers doesn't matter.
- Comparing every pair. It gives the right answer but takes
O(n²)time, which is too slow at 100,000 elements. - Using
<=instead of<in the first test. Then a tie would reset the list instead of extending it, and the earlier pairs would be lost. - Forgetting the reset on a new record. Pairs with a larger gap would stay in the answer.
- Distinct values. The statement guarantees no duplicates. With duplicates, the minimum gap would be 0 and the condition
a < bwould no longer fit.
Testing
[4, 2, 1, 3]gives[[1, 2], [2, 3], [3, 4]].[1, 3, 6, 10, 15]gives[[1, 3]].[3, 8, -10, 23, 19, -4, -14, 27]gives[[-14, -10], [19, 23], [23, 27]].[5, 1]gives[[1, 5]].[10, 9, 100, 11]gives[[9, 10], [10, 11]].