Start learning free

LESSON 221 OF 414 · BRUTE FORCE

Minimum Absolute Difference in Scala: Nested Loops and Brute Force

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

    SCALA · BRUTE FORCE

    Minimum Absolute Difference in Scala, explained

    Problem overview

    The method minimumAbsDifference receives an array nums of distinct integers. It must find the smallest absolute difference between any two elements, then return every pair of elements that differ by exactly that amount. Each pair is written [a, b] with a < b, and the pairs come back in ascending order.

    For nums = [4, 2, 1, 3], the smallest difference is 1. The pairs that reach it are [1, 2], [2, 3] and [3, 4]. For [3, 8, -10, 23, 19, -4, -14, 27] the smallest difference is 4, and three pairs reach it: [-14, -10], [19, 23] and [23, 27].

    Approach: check every pair

    This chapter is about nested loops, so the idea is the direct one. Take every pair of positions (i, j) with i < j, compute the absolute difference of the two values, and keep track of the best difference seen so far together with the pairs that reached it.

    For each new difference there are three cases:

    • It is smaller than the best so far. Every pair collected up to now is out of the running, so the list restarts with this one pair.
    • It equals the best so far. The pair is added to the list.
    • It is larger. Nothing happens.

    Looping over j from i + 1 visits each unordered pair exactly once. The pairs are discovered in the order of their positions in nums, not their values, so the list is sorted at the end.

    The cost is O(n²) comparisons, which is fine for the small arrays that teach the pattern. The constraints allow up to 100,000 elements, which would mean about five billion pairs. The next lesson on this problem sorts first and compares only neighbours, a much faster route.

    Step by step

    1. Start the best difference with a real value. minDiff begins as the difference between the first two elements. The statement guarantees at least two elements, so there is always a first pair to compare, and no "infinity" placeholder is needed.
    2. Create an empty collection for the pairs. It is an ArrayBuffer, a growable array, because the number of pairs isn't known in advance.
    3. Loop over the first element of every pair with i from 0 until nums.length.
    4. Loop over the second element with j from (i + 1) until nums.length, so no pair is repeated and no element is paired with itself.
    5. Compute the difference curDiff as the absolute value of nums(i) - nums(j).
    6. On a new record (curDiff < minDiff), store the new minDiff and replace the buffer with a new one holding only this pair.
    7. Write the pair as smaller first. Either element might be the smaller one, so Math.min and Math.max put them in the required order.
    8. On a tie (curDiff == minDiff), append the pair to the buffer.
    9. Sort the pairs by their first element, so the output order doesn't depend on the order of the input.
    10. Return the buffer as an array. The method's result type is Array[Array[Int]].

    The solution in Scala

    class Solution {
        def minimumAbsDifference(nums: Array[Int]): Array[Array[Int]] = {
            var minDiff = Math.abs(nums(0) - nums(1))
            var minDiffPairs = ArrayBuffer[Array[Int]]()
            for (i <- 0 until nums.length) {
                for (j <- (i + 1) until nums.length) {
                    val curDiff = Math.abs(nums(i) - nums(j))
                    if (curDiff < minDiff) {
                        minDiff = curDiff
                        minDiffPairs = ArrayBuffer(Array(Math.min(nums(i), nums(j)), Math.max(nums(i), nums(j))))
                    } else if (curDiff == minDiff) {
                        minDiffPairs += Array(Math.min(nums(i), nums(j)), Math.max(nums(i), nums(j)))
                    }
                }
            }
            minDiffPairs = minDiffPairs.sortBy(pair => pair(0))
            minDiffPairs.toArray
        }
    }
    

    The Scala details worth knowing:

    • ArrayBuffer must be imported. It lives in scala.collection.mutable, so a standalone file needs import scala.collection.mutable.ArrayBuffer at the top.
    • var for values that change. minDiff and minDiffPairs are reassigned inside the loops, so they are var. curDiff never changes after it is computed, so it is a val.
    • Indexing uses parentheses. An array element is nums(i), not nums[i]. Square brackets in Scala hold type parameters, as in ArrayBuffer[Array[Int]].
    • for (i <- 0 until n) loops over 0 to n - 1. The range excludes its end, while 0 to n would include n and read past the array.
    • += on a buffer appends. On a val or var holding an ArrayBuffer, buffer += x adds x to the same buffer. That is different from minDiff = curDiff, which rebinds a variable.
    • sortBy returns a new buffer. It doesn't sort in place, so the result is assigned back to minDiffPairs. It sorts by the key the lambda returns, here the first element of each pair.
    • The last expression is the result. minDiffPairs.toArray has no return in front of it, and none is needed.

    Complexity

    Time: O(n²). The two loops visit n(n - 1)/2 pairs, each in constant time. The final sort costs O(k log k) for k tied pairs. Since k is below n, the quadratic part dominates.

    Space: O(k) for the buffer of k pairs, which is at most n - 1 because the values are distinct.

    Edge cases and common mistakes

    • Only two elements. [5, 1] gives [[1, 5]]. The initial minDiff is the difference of that same pair, so when the loops reach it, curDiff == minDiff and the else if branch adds it. The first pair is always recorded this way unless a smaller difference replaces it.
    • Forgetting to reset on a record. If the new-record branch only updated minDiff and appended, the buffer would keep pairs from larger differences.
    • Writing the pair in input order. For [10, 1, 4] the pair of 4 and 1 must be written [1, 4], not [4, 1].
    • Starting j at i. An element paired with itself has difference 0, which would beat every real pair.
    • Skipping the sort. The pairs are found in input order. For [4, 2, 1, 3] the buffer ends up as [3, 4], [1, 2], [2, 3], which only becomes the required ascending order after sortBy.

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