Card 04/ 08
Two Pointers Example: Triples of Weights That Make a Target
Fix one value, run the pair search on the rest, and skip any value equal to the one before so no triple is counted twice.
Finding every distinct triple that adds up to a target takes a sorted array, one fixed index and an opposite-ends pair search on the values after it. A gym has plates of 2, 2, 3, 3, 4, 5 and 6 kilograms, and the question is every distinct set of three plates weighing 10 kilograms in all, where two sets that use the same weights count once.
How many distinct triples weigh 10 kilograms?
Three: 2 2 6, 2 3 5 and 3 3 4. A search that does not skip repeated values reports four, because the second 2 finds 2 3 5 again.
Sort the plates, then fix each index i in turn and search the values after it for a pair adding up to 10 − values[i] with two pointers. Each fixed index costs one O(n) pass, so the whole search is O(n²) time for n plates, against O(n³) for checking every triple.
Repeated weights are skipped in three places. A fixed value equal to the one before it is skipped, since every triple it could start was already found. After a triple is found, the left pointer moves past values equal to the one it used, and the right pointer past values equal to its own, so the same pair is not reported again for this fixed value. Sorting is what makes all three work, because it puts equal values next to each other.
On the sorted plates 2 2 3 3 4 5 6, each fixed value either finds its pairs or is skipped as a repeat.
| Fixed value | Pairs found | Skipped as duplicate |
|---|---|---|
| 2, index 0 | 2 + 6, 3 + 5 | No |
| 2, index 1 | none searched | Yes |
| 3, index 2 | 3 + 4 | No |
| 3, index 3 | none searched | Yes |
| 4, index 4 | none | No |
The listing runs the search with and without the skips, on the same plates.
def triples(weights: list[int], target: int, skip_repeats: bool) -> list[tuple[int, int, int]]:
values = sorted(weights)
found = []
for i in range(len(values) - 2):
if skip_repeats and i > 0 and values[i] == values[i - 1]:
continue
left, right = i + 1, len(values) - 1
while left < right:
total = values[i] + values[left] + values[right]
if total == target:
found.append((values[i], values[left], values[right]))
left += 1
right -= 1
while skip_repeats and left < right and values[left] == values[left - 1]:
left += 1
while skip_repeats and left < right and values[right] == values[right + 1]:
right -= 1
elif total < target:
left += 1
else:
right -= 1
return found
weights = [2, 2, 3, 3, 4, 5, 6]
print("with the skips:", triples(weights, 10, True))
print("without them: ", triples(weights, 10, False))with the skips: [(2, 2, 6), (2, 3, 5), (3, 3, 4)]
without them: [(2, 2, 6), (2, 3, 5), (2, 3, 5), (3, 3, 4)]Without the skips, the second 2 repeats the first one's search and reports 2 3 5 a second time. With them, each set of weights appears once. The cost stays O(n²) time either way, plus O(n log n) for the sort, and the search needs O(1) extra space beyond the list of answers. Fixing one value and searching the rest extends to four values with a second fixed index, at O(n³).
← → move through sections and cards