Find 2 numbers in an array whose sums add up to target . A variant of this question is finding the largest 2 numbers that are <= target There's 2 approaches to it
If question asks simply find any 2 numbers that = target you can use a map to find if the target - k is in the map. if k == (target - k) then there must be 2 occurrences of k in the array
def twoSum(nums: Array[Int], target: Int): Option[(Int, Int)] = {
val m = nums.foldLeft(Map.empty[Int, Int]) { (m, n) =>
m.get(n).fold(m + (n -> 1))(v => m + (n -> (v + 1)))
}
m.find {
case (k, v) =>
val other = target - k
if (other == k) {
v == 2
} else m.contains(k)
}.map{case (k, _) => k -> (target - k)}
}
Find all pairs
Approach 1
def findPairs(is: Vector[Int], target: Int): List[(Int, Int)] = {
val m = is.foldLeft(Map.empty[Int, Int]){(m, i) =>
m.get(i).fold(m + (i -> 1))(v => m + (i -> (v + 1)))
}
def go(out: List[(Int, Int)], m: Map[Int, Int]): List[(Int, Int)] =
m.headOption.fold(out){ case (i, freq) =>
val other = target - i
if (other == i) {
if (freq > 1)
go(
0.until(freq / 2).foldLeft(out)((out, _) => (i -> i) :: out),
m.tail
)
else go(out, m.tail)
} else {
m.get(other).fold(go(out, m.tail))(freqOther =>
go(
0.until(Math.min(freq, freqOther)).foldLeft(out)((out, _) => (i -> other) :: out),
m.tail - other
)
)
}
}
go(List.empty, m)
}
Approach 2
def findPairs(is: Vector[Int], target: Int): List[(Int, Int)] = {
@tailrec
def go(sorted: Vector[Int], out: List[(Int, Int)]): List[(Int, Int)] = {
if (sorted.length == 1) out
else if (sorted.head + sorted.last == target)
go(
// or you can maintain `i` and `j` pointers in Approach 1 in
// Find a single pair above
sorted.tail.dropRight(1),
(sorted.head -> sorted.last) :: out
)
else if (sorted.head + sorted.last > target)
go(
sorted.dropRight(1),
out
)
else
go(
sorted.tail,
out
)
}
go(is.sorted, List.empty)
}