Start learning free

LESSON 222 OF 415 · BRUTE FORCE

Minimum Absolute Difference in Swift: Brute Force with Nested Loops

Practice O(n2) Read the full explanation

Given an array of distinct integers nums, find all pairs of elements with the minimum absolute difference of any two elements.

Return a list of pairs in ascending order (with respect to pairs), each pair [a, b] follows

  • a, b are from nums
  • a < b
  • b - a equals the minimum absolute difference of any two elements in nums

Example 1:

Input: nums = [4,2,1,3]
Output: [[1,2],[2,3],[3,4]]
Explanation: The minimum absolute difference is 1. List all pairs with difference equal to 1 in ascending order.

Example 2:

Input: nums = [1,3,6,10,15]
Output: [[1,3]]

Example 3:

Input: nums = [3,8,-10,23,19,-4,-14,27]
Output: [[-14,-10],[19,23],[23,27]]

Constraints:

  • 2 <= nums.length <= 105
  • -106 <= nums[i] <= 106

STEP 1 OF 10
    Ctrl + Enter to check

    SWIFT · BRUTE FORCE

    Minimum Absolute Difference in Swift, explained

    Problem overview

    The method minimumAbsDifference receives an array nums of distinct integers. Among all pairs of elements, it must find the smallest absolute difference, then return every pair that reaches it. Each pair is written [a, b] with a < b, and the list of pairs is ordered by its first value.

    For nums = [4, 2, 1, 3] the smallest difference is 1. The pairs that reach it are 1 and 2, 2 and 3, and 3 and 4, so the answer is [[1, 2], [2, 3], [3, 4]]. For [1, 3, 6, 10, 15] only 1 and 3 are two apart, and every other pair is further, so the answer is [[1, 3]].

    Approach: try every pair

    This lesson uses the most direct idea. Look at every pair of positions, compute its difference, and keep the best pairs seen so far. With n elements there are about n * (n - 1) / 2 pairs, so the work grows with n². That is fine for small arrays and is a good way to get the logic right. A later lesson on the same problem sorts the array first, because after sorting the closest pair must sit side by side.

    The bookkeeping is the interesting part. You track the smallest difference so far, minDiff, and the pairs that achieve it, minDiffPairs. For each new pair there are three cases:

    • its difference is smaller than minDiff: a new record, so every pair collected before is now wrong and gets thrown away;
    • its difference equals minDiff: a tie, so the pair is added to the list;
    • its difference is larger: ignore it.

    Step by step

    1. Start minDiff with a real value. Use the difference of the first two elements, abs(nums[0] - nums[1]). A made-up starting value like a huge number would also work, but a real pair needs no special "nothing yet" case. Also create minDiffPairs as an empty [[Int]].
    2. Loop i over every position in 0..<nums.count. It picks the first element of a pair.
    3. Loop j over the positions after i, in (i + 1)..<nums.count. Starting after i means each pair is looked at once, and no element is paired with itself.
    4. Compute curDiff as abs(nums[i] - nums[j]). It is a constant because it never changes inside the iteration.
    5. Handle a new record. If curDiff < minDiff, update minDiff and replace minDiffPairs with a list holding only this pair.
    6. Store the pair in the right order. The array may list the larger number first, so the pair is built as [min(nums[i], nums[j]), max(nums[i], nums[j])].
    7. Handle a tie with else if curDiff == minDiff and append the pair in the same form.
    8. Sort the result after both loops, by each pair's first value, and return it.

    The solution in Swift

    class Solution {
        func minimumAbsDifference(_ nums: [Int]) -> [[Int]] {
            var minDiff = abs(nums[0] - nums[1])
            var minDiffPairs = [[Int]]()
            for i in 0..<nums.count {
                for j in (i + 1)..<nums.count {
                    let curDiff = abs(nums[i] - nums[j])
                    if curDiff < minDiff {
                        minDiff = curDiff
                        minDiffPairs = [[min(nums[i], nums[j]), max(nums[i], nums[j])]]
                    } else if curDiff == minDiff {
                        minDiffPairs.append([min(nums[i], nums[j]), max(nums[i], nums[j])])
                    }
                }
            }
            minDiffPairs.sort { $0[0] < $1[0] }
            return minDiffPairs
        }
    }
    

    The Swift details worth knowing:

    • Parameters are constants. nums can be read but not changed, which is fine here because the function only reads it. The two var declarations are the values that change.
    • Half-open ranges. 0..<nums.count stops before nums.count. On the last pass of the outer loop, (i + 1)..<nums.count becomes nums.count..<nums.count, an empty range, so the inner loop just does nothing. A range whose start is above its end, such as 3..<2, crashes the program instead.
    • [[Int]]() creates an empty array of integer arrays, and minDiffPairs = [[x, y]] replaces it with an array holding one pair.
    • The trailing closure. sort { $0[0] < $1[0] } sorts the array in place. $0 and $1 are the two pairs being compared, and the closure returns true when the first should come earlier. Comparing only the first values is enough here, because the elements are distinct, so two different pairs never start with the same number.
    • min and max are global functions that take two values and return the smaller or larger one.
    • abs works on any signed number. With values this small the subtraction cannot overflow. An overflowing Int subtraction would crash the program in Swift instead of wrapping around.

    Complexity

    Time: O(n²) for the loops. The nested loops look at about n * (n - 1) / 2 pairs, and each takes constant time. The final sort of the pairs adds O(k log k), where k is the number of pairs kept, which is at most n - 1, so the loops dominate.

    Space: O(k). Only the kept pairs are stored, and there are at most n - 1 of them.

    Edge cases and common mistakes

    • Not discarding old pairs on a new record. If the record branch appended instead of replacing, pairs with a larger difference would stay in the answer. For [4, 2, 1, 3] the first pair seen is 4 and 2, with difference 2, and it must disappear once a difference of 1 appears.
    • Skipping the final sort. The pairs are collected in the order the loops find them. For [4, 2, 1, 3] that order is [[3, 4], [1, 2], [2, 3]], which is not ascending.
    • Starting j at i. The pair of an element with itself has difference 0, which would always win.
    • Forgetting min and max. For the pair 4 and 3 the loops meet 4 first, so [nums[i], nums[j]] would give [4, 3] where [3, 4] is wanted.
    • Fewer than two elements. nums[1] doesn't exist for an array of one element, and reading it crashes. The lesson guarantees at least two elements.

    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, 2] gives [[2, 5]].
    Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free