Problem overview
The function minimumAbsDifference receives a slice of distinct integers, nums, with at least two elements. It must find the smallest absolute difference between any two elements and return every pair [a, b] with a < b whose difference b - a equals that minimum. The pairs must come in ascending order.
For nums = [4, 2, 1, 3], the smallest difference is 1, reached by [1, 2], [2, 3] and [3, 4]. For [1, 3, 6, 10, 15] the minimum is 2, and only [1, 3] reaches it.
Approach: only neighbours can be the closest pair
Comparing every pair costs O(n²), which is too slow for slices of up to 100,000 elements. Sorting makes it unnecessary. In a sorted slice, if two values x < z are not adjacent, some value y lies between them in the order. Then y - x and z - y are each no larger than z - x, so the non-adjacent pair can never beat the pairs formed with y. A pair of values at the minimum distance is therefore always a pair of neighbours. Since the values are distinct, both inner gaps are also strictly smaller than the outer one, so a non-adjacent pair can never even tie for the minimum.
So the algorithm sorts, then looks at each adjacent pair once, keeping the best difference and the list of pairs that reach it. Because the slice is sorted, the pairs are produced already in ascending order, and each has a < b.
On the third example, [3, 8, -10, 23, 19, -4, -14, 27], the sorted slice is [-14, -10, -4, 3, 8, 19, 23, 27]. The neighbour gaps are 4, 6, 7, 5, 11, 4 and 4. The minimum is 4, shared by [-14, -10], [19, 23] and [23, 27].
Tracing the loop
On the sorted third example, minDiff starts at 4 (from -10 minus -14), and minDiffPairs is empty. The first loop pass, i = 1, computes curDiff = 4. That is not smaller than 4, but it equals it, so the pair [-14, -10] is appended. This is why the loop starts at index 1 even though minDiff was already computed from the same pair: the pair itself still has to be recorded. The next three gaps (6, 7, 5) change nothing. The gap 11 changes nothing either. Then 4 appears twice more, appending [19, 23] and [23, 27].
A slice where a smaller gap appears later shows the reset. For [1, 10, 11], the first gap is 9 and the pair [1, 10] is recorded. At i = 2, the gap is 1, which is smaller, so minDiff becomes 1 and the list is replaced by [[10 11]].
Step by step
- Sort in place with
sort.Ints(nums), so the closest pairs are adjacent. - Initialise the best difference and the result. Declare
minDiff := nums[1] - nums[0], the gap of the first pair, which is a real gap rather than a made-up "infinity". DeclareminDiffPairs := [][]int{}, an empty slice of slices. - Loop over the neighbours with
ifrom 1 up tolen(nums) - 1. Each pass looks atnums[i-1]andnums[i]. - Compute the gap in
curDiff := nums[i] - nums[i-1]. It is never negative after sorting, so no absolute value is needed. - Found a smaller gap. If
curDiff < minDiff, updateminDiffand replace the result with a new list holding only this pair. All earlier pairs were worse. - Found an equal gap. In an
else if curDiff == minDiffbranch, append the pair to the list. - Return
minDiffPairsafter the loop.
The solution in Go
func minimumAbsDifference(nums []int) [][]int {
sort.Ints(nums)
minDiff := nums[1] - nums[0]
minDiffPairs := [][]int{}
for i := 1; i < len(nums); i++ {
curDiff := nums[i] - nums[i-1]
if curDiff < minDiff {
minDiff = curDiff
minDiffPairs = [][]int{{nums[i-1], nums[i]}}
} else if curDiff == minDiff {
minDiffPairs = append(minDiffPairs, []int{nums[i-1], nums[i]})
}
}
return minDiffPairs
}
Go details:
[][]intis a slice whose elements are themselves slices. In the composite literal[][]int{{nums[i-1], nums[i]}}, the inner[]inttype is allowed to be omitted, which is why the reset line can use plain braces. When appending, you have to write[]int{...}explicitly, sinceappendis an ordinary function call, not a literal.- The result starts as an empty, non-
nilslice. Because the first pair always sets the starting gap, at least one pair is always appended or kept, so the function never returns an empty list for valid input. - Each pair holds copies of the two integers. Changing
numslater would not change the pairs. sort.Intsreorders the caller's slice, and the file needsimport "sort".
Complexity
Time: O(n log n). Sorting dominates. The loop is a single O(n) pass.
Space: O(1) beyond the output. The scan keeps a few integers. The returned list of pairs can hold up to n - 1 pairs, which is required output and not working memory.
Edge cases and common mistakes
- Exactly two elements.
[5, 1]sorts to[1, 5], the loop makes one pass and the result is[[1 5]]. - Fewer than two elements. The solution reads
nums[1]at the start, so a one-element slice panics with "index out of range [1] with length 1". The problem guarantees at least two. - Several pairs tied for the minimum. Use
else if curDiff == minDiffto append them. Without it, only the first pair would be returned. - Forgetting to reset. When a smaller gap appears, the old pairs must be discarded. Appending instead of replacing would return pairs with a larger difference.
- Starting
minDiffat 0. With distinct values, no gap would ever be smaller than 0, so no pair would be recorded. - Comparing the original order. Without sorting, adjacent elements are not the closest ones: in
[4, 2, 1, 3], the adjacent gaps are 2, 1 and 2, which miss the pair[3, 4].
Small inputs to try in the editor above: [4, 2, 1, 3] should give [[1 2] [2 3] [3 4]], and [1, 3, 6, 10, 15] should give [[1 3]].