Problem overview
The function arrayDiff receives two IntArrays, first and second. It must find one number from each array so that the absolute difference between them is as close to zero as possible, and return those two numbers as an IntArray, the one from first before the one from second.
For first = [4, 1, 8] and second = [12, 3, 17] the closest pair is 4 and 3, because abs(4 - 3) = 1 and no other pair is closer. The answer is [4, 3]. The statement asks for O(n log n) time and O(1) extra space.
Approach: sort both arrays, then sweep
Trying every pair costs O(n * m) comparisons. Sorting removes most of that work.
Once both arrays are in ascending order, keep one pointer in each: i in first and j in second. Look at the pair they point to and record its difference if it beats the best so far. Then decide which pointer to move. If first[i] is the smaller of the two, the only way to bring the values closer is to raise it, so advance i. Advancing j would pick an even larger second value, and the gap could only grow. In the other case, advance j.
Every move discards a value that can't form a closer pair with anything still ahead, so the sweep is linear. The sorting is O(n log n), which makes it the dominant cost.
On the example, the sorted arrays are [1, 4, 8] and [3, 12, 17]:
- 1 and 3 differ by 2, so the best is
[1, 3]. 1 is smaller, soimoves. - 4 and 3 differ by 1, a new best:
[4, 3]. Now 3 is smaller, sojmoves. - 4 and 12 differ by 8, and 8 and 12 differ by 4. Neither beats 1, and
firstruns out.
The result is [4, 3].
Step by step
- Sort
firstin place withsort(). The pointer sweep depends on ascending order. - Sort
secondthe same way. - Create the pointers.
iandjarevars starting at 0, one per array. - Create
smallestDiff, starting atInt.MAX_VALUEso that the first real difference is always smaller. - Create
result, avarholding an emptyIntArrayfromintArrayOf(). It is replaced whenever a closer pair turns up. - Loop while both pointers are inside their arrays. When either array is used up, no more pairs can be formed.
- Compute
diffas the absolute value offirst[i] - second[j]. - Record a better pair. If
diff < smallestDiff, update bothsmallestDiffandresult. - Move the pointer at the smaller value: if
first[i] < second[j], incrementi. - Otherwise increment
j. This covers the case wheresecond[j]is smaller and the case where the two values are equal. - Return
result.
The solution in Kotlin
class Solution {
fun arrayDiff(first: IntArray, second: IntArray): IntArray {
first.sort()
second.sort()
var i = 0
var j = 0
var smallestDiff = Int.MAX_VALUE
var result = intArrayOf()
while (i < first.size && j < second.size) {
val diff = Math.abs(first[i] - second[j])
if (diff < smallestDiff) {
smallestDiff = diff
result = intArrayOf(first[i], second[j])
}
if (first[i] < second[j]) {
i++
} else {
j++
}
}
return result
}
}
Kotlin details worth knowing:
sort()changes the caller's arrays. On anIntArray,sort()reorders the array itself and returns nothing. After the call, the arrays passed in are sorted: with the example,firstbecomes[1, 4, 8]andsecondbecomes[3, 12, 17].sorted()would return a newList<Int>and leave the array alone. Sorting in place is what keeps the extra space small.Math.abs. This is the Java library function, which Kotlin code can call directly. The Kotlin form iskotlin.math.abs, which needs an import; both work onInt.intArrayOf()with no arguments makes an emptyIntArray. The variable's type is inferred asIntArray, so it can later be assigned a two-element array.Int.MAX_VALUEis 2147483647, the largestInt.- Overflow in the subtraction.
Intis 32 bits and wraps silently. A difference such as2000000000 - (-2000000000)doesn't fit and wraps to a negative number, whose absolute value is wrong. Values in the lesson's tests are small enough that this doesn't arise. ALongsubtraction would be safe for anyIntinputs.
Complexity
Let n and m be the two sizes.
Time: O(n log n + m log m), which is O(n log n) for inputs of similar size. The sweep takes at most n + m steps, since each iteration moves one pointer forward and no pointer moves back.
Space: O(1) beyond the inputs. The function keeps a few integers and one two-element result. The sort works in place on the arrays, which is why they end up modified.
Edge cases and common mistakes
- An empty array. The loop never runs and the function returns an empty array.
- Ties. The comparison is a strict
<, so a later pair with the same difference does not replace an earlier one. For[1, 5]and[3], both pairs differ by 2, and the answer is[1, 3]. - Equal values.
[1]and[1]give[1, 1], a difference of 0. - Forgetting to sort. The rule for moving pointers is only valid on ascending arrays.
- Moving the wrong pointer. Advancing the pointer at the larger value can only increase the gap, so the sweep could skip the best pair.
- Result order. The pair is always (value from
first, value fromsecond), even when the second is smaller:[5, 2]and[7, 3]give[2, 3].
Testing
first = [4, 1, 8],second = [12, 3, 17]gives[4, 3].first = [1, 5],second = [3]gives[1, 3].first = [5, 2],second = [7, 3]gives[2, 3].first = [1],second = [1]gives[1, 1].first = [],second = [3]gives[].