Searching & Sorting · 13 of 36
Count triplet with sum smaller than a given value
Count how many ways you can pick three different numbers whose total stays under a limit.
- 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 n different numbers and a limit sum. Pick three numbers from three different positions. Count how many such groups of three add up to less than sum.
You only count the groups. You do not need to list them.
A story to remember it
Picture it
Sorted prices with a budget under 12. With i on 1 and l on 3, the group 1 + 3 + 7 = 11 fits, so 1 + 3 + 4 and 1 + 3 + 5 fit too. That is r - l = 3 groups in one look.
- looking now
- done
Examples
A normal case
- In
arr = [5, 1, 3, 4, 7], sum = 12- Out
4
The groups are 1 + 3 + 4 = 8, 1 + 3 + 5 = 9, 1 + 3 + 7 = 11 and 1 + 4 + 5 = 10.
With negatives
- In
arr = [-2, 0, 1, 3], sum = 2- Out
2
-2 + 0 + 1 = -1 and -2 + 0 + 3 = 1. The group -2 + 1 + 3 = 2 is not smaller than 2.
All negative
- In
arr = [-5, -4, -3, -2], sum = -10- Out
2
-5 - 4 - 3 = -12 and -5 - 4 - 2 = -11 are under -10. The other two groups are not.
Every group fits
- In
arr = [1, 2, 3, 4], sum = 100- Out
4
Four numbers make 4 groups of three, and all of them are small.
Too few numbers
- In
arr = [1, 2], sum = 10- Out
0
You cannot pick three numbers from two.
Way 1: the simple way
Try every group of three positions i < j < k and count the ones whose sum is under the limit.
class Solution {
public long countTriplets(int[] arr, int sum) {
int n = arr.length;
long count = 0;
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] < sum) count++;
return count;
}
}How long it takes: there are about n^3 / 6 groups, so O(n^3). Extra room: O(1).
Way 2: the clever way
Sort the numbers. Fix the smallest member of the group, then find the other two with two fingers that walk toward each other.
- Sort the array.
- For each
i, setl = i + 1andr = n - 1. - If
arr[i] + arr[l] + arr[r] < sum, thenarr[r]can be swapped for anything betweenlandr, because those are all smaller. That isr - lgroups. Add them all and movelright. - Otherwise
arr[r]is too big witharr[l], and with any biggerltoo. Moverleft. - Stop when the fingers meet, then move
i.
Fix one number, squeeze the other two
01 / 13
Sort first: [1, 3, 4, 5, 7]. We want groups of three with a sum smaller than 12.
- target
- < 12
- count
- 0
- looking now
- checking
- done
- rejected
- ignored
import java.util.*;
class Solution {
public long countTriplets(int[] arr, int sum) {
int n = arr.length;
Arrays.sort(arr);
long count = 0;
for (int i = 0; i < n - 2; i++) {
int l = i + 1, r = n - 1;
while (l < r) {
if (arr[i] + arr[l] + arr[r] < sum) {
count += r - l; // arr[r] can be swapped for anything between l and r
l++;
} else {
r--; // arr[r] is too big here
}
}
}
return count;
}
}How long it takes: sorting is O(n log n). For each i the two fingers together take at most n steps, so the loops are O(n^2). Extra room: O(1) besides the sort.
Watch out for
- Forgetting to sort. The "everything between is smaller" idea only holds on a sorted array.
- Moving
rafter a hit. A hit meanslis used up with every partner, so movel. - Big numbers: three values near two billion overflow an
intsum. Cast tolongwhen the values can be large. - The answer itself can be about
n^3 / 6. Return along.
What interviewers ask next
- Can you print the groups instead? Yes, but there can be
O(n^3)of them, so listing them costs that much no matter what. - Count groups with sum greater than
sum? Same fingers: when the group is too big, addr - land moverleft. - Count groups with sum between
lowandhigh? Count those underhigh + 1and subtract those underlow. - What about pairs with sum under
k? Same idea with two fingers and no fixedi,O(n log n). - Why not use a hash map like in 3-sum? A hash map answers "is this exact number here", not "how many are smaller". Sorting does the counting for free.
Remember this
Grown-up words: the two fingers are the two pointer technique, and counting the whole stretch at once works because the array is sorted, so all values between the pointers are smaller.