Array · 17 of 36
Find the triplet that sum to a given value
Find three numbers in a list that add up to a target, by fixing one and squeezing the other two from both ends.
- Simple way
- time O(n^3)
room O(1) - Clever way
- time O(n^2)
room O(1)
The problem, simply
You get an array of numbers and a target. Find three numbers at three different positions that add up to the target. Give back the three numbers, or say there are none.
A story to remember it
Picture it
Sorted prices. With 4 fixed, the two hands meet the pair 8 and 10: 4 + 8 + 10 = 22.
- answer
Examples
A normal case
- In
arr = [1, 4, 45, 6, 10, 8], target = 22- Out
[4, 8, 10]
4 + 8 + 10 = 22.
Negative numbers
- In
arr = [-1, 0, 1, 2, -1, -4], target = 0- Out
[-1, -1, 2]
Sorted: -4, -1, -1, 0, 1, 2. Fixing -1, the hands find -1 and 2. ([-1, 0, 1] also works.)
One number cannot be used three times
- In
arr = [5, 1, 2], target = 15- Out
none
5 + 5 + 5 would be 15, but there is only one 5. Positions must be different.
Too few numbers
- In
arr = [3, 9], target = 12- Out
none
You need three numbers. With fewer than three, return none right away.
Way 1: the simple way
Try every group of three positions.
class Solution {
public int[] findTriplet(int[] arr, int target) {
int n = arr.length;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
for (int k = j + 1; k < n; k++)
if (arr[i] + arr[j] + arr[k] == target)
return new int[] { arr[i], arr[j], arr[k] };
return new int[0]; // none found
}
}How long it takes: about n^3 / 6 groups, O(n^3). Extra room: O(1).
Way 2: the clever way
Sort, then for each first number solve "find two numbers with a given sum" using two hands.
- Sort the array.
- For each
i:need = target - arr[i]. Putl = i + 1andr = n - 1. - While
l < r: ifarr[l] + arr[r] == need, done. If it is less, movelright. If it is more, moverleft.
Why moving a hand is safe: if arr[l] + arr[r] is too small, then arr[l] with any partner left of r is even smaller. So arr[l] can never be part of the answer, and we drop it. The same idea works for r when the sum is too big.
Fix one number, squeeze the other two
01 / 11
Sort first: 1, 4, 6, 8, 10, 45. Now small numbers are on the left and big ones on the right.
- target
- 22
- looking now
- checking
- answer
- rejected
class Solution {
public int[] findTriplet(int[] arr, int target) {
int[] a = arr.clone();
Arrays.sort(a);
int n = a.length;
for (int i = 0; i < n - 2; i++) {
int need = target - a[i]; // the other two must make this
int l = i + 1, r = n - 1;
while (l < r) {
int sum = a[l] + a[r];
if (sum == need) return new int[] { a[i], a[l], a[r] };
if (sum < need) l++; // too small: drop the left one
else r--; // too big: drop the right one
}
}
return new int[0];
}
}How long it takes: sorting is O(n log n), and for each of the n first numbers the hands walk at most n steps, so O(n^2) in total. Extra room: O(1) besides the copy and the sort.
Watch out for
- Forgetting to sort. The squeeze only makes sense on a sorted array.
- When asked for all different triplets, skip repeated values: skip
iifa[i] == a[i - 1], and after a match movelandrpast copies of the values you used. - Sums can overflow
intwith huge values. Uselongforsumandneedif needed.
What interviewers ask next
- Return all unique triplets that sum to 0 (LeetCode 3Sum)? Same loops, keep going after a match, and skip repeated values as above. Still
O(n^2). - Closest sum to the target? Same squeeze, but remember the sum with the smallest distance to the target instead of stopping.
- Without sorting? For each
i, use a hash set for the two sum part on the rest of the array.O(n^2)time butO(n)extra room. - Four numbers? Fix two numbers with two loops and squeeze the other two:
O(n^3). In generalknumbers takeO(n^(k-1)). - Count triplets with sum less than the target? When
a[l] + a[r] < need, everyr'betweenl + 1andralso works, so addr - land movel.
Remember this
Grown-up words: the hands are two pointers, the inner part is the two sum problem on a sorted array, and the whole thing is the 3Sum pattern.