A clear explanation of the 3Sum Smaller problem using sorting and the two-pointer technique.
Problem Restatement
We are given:
- An integer array
nums - An integer
target
We need to count how many index triplets satisfy:
i < j < kand:
Return the number of such triplets.
The constraints are 0 <= nums.length <= 350, -100 <= nums[i] <= 100, and -100 <= target <= 100. (github.com)
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer array nums and integer target |
| Output | Number of valid triplets |
| Valid triplet | i < j < k |
| Condition | Sum of the three numbers is smaller than target |
Example function shape:
def threeSumSmaller(nums: list[int], target: int) -> int:
...Examples
Example 1:
nums = [-2, 0, 1, 3]
target = 2Valid triplets:
| Triplet | Sum |
|---|---|
(-2, 0, 1) | -1 |
(-2, 0, 3) | 1 |
Triplet:
(-2, 1, 3)has sum:
2which is not strictly smaller than 2.
Answer:
2Example 2:
nums = []
target = 0No triplets exist.
Answer:
0Example 3:
nums = [0, 0, 0]
target = 1The only triplet has sum:
0which is smaller than 1.
Answer:
1First Thought: Try Every Triplet
The direct approach is to check every possible triplet.
class Solution:
def threeSumSmaller(self, nums: list[int], target: int) -> int:
n = len(nums)
count = 0
for i in range(n):
for j in range(i + 1, n):
for k in range(j + 1, n):
if nums[i] + nums[j] + nums[k] < target:
count += 1
return countThis works, but it checks too many combinations.
Problem With Brute Force
Three nested loops give:
With n = 350, this becomes unnecessarily expensive.
We need a faster way to count many triplets at once.
Key Insight
Sorting allows us to use the two-pointer technique.
Suppose the array is sorted:
nums = [-2, 0, 1, 3]Fix the first number:
nums[i] = -2Now we need pairs:
Use two pointers:
| Pointer | Meaning |
|---|---|
left | Starts after i |
right | Starts at the end |
The important observation is this:
If:
then every index between left and right also works with left.
Why?
Because the array is sorted.
So:
nums[left + 1] <= nums[right]Replacing nums[right] with a smaller value keeps the sum smaller than target.
Therefore we can count all these triplets at once:
right - leftThis is the core optimization.
Algorithm
- Sort the array.
- Fix the first index
i. - Use two pointers:
left = i + 1right = n - 1
- While
left < right:- Compute the triplet sum.
- If the sum is smaller than
target:- Add:to the answer.
right - left - Move
leftrightward.
- Add:
- Otherwise:
- Move
rightleftward.
- Move
Walkthrough
Use:
nums = [-2, 0, 1, 3]
target = 2The array is already sorted.
Start:
i = 0
nums[i] = -2
left = 1
right = 3Current sum:
-2 + 0 + 3 = 1Since:
the triplet works.
Because the array is sorted, every index between left and right also works with left.
That gives:
right - left = 2valid triplets:
(-2, 0, 1)
(-2, 0, 3)Add 2 to the answer.
Move left:
left = 2Now:
-2 + 1 + 3 = 2This is not strictly smaller than 2.
Move right leftward.
Now left == right, so stop.
Continue with:
i = 1No more valid triplets exist.
Final answer:
2Correctness
The array is sorted before processing.
Fix some index i.
The algorithm searches for pairs (left, right) such that:
If this condition is true, then every index k satisfying:
left < k <= rightalso forms a valid triplet with i and left.
This follows from sorting:
nums[k] <= nums[right]Therefore replacing nums[right] with any smaller value keeps the total sum below target.
So exactly:
right - leftnew triplets are counted correctly.
If instead the sum is too large, then using the same right with any larger left index would only increase the sum further. Therefore decreasing right is the only way to possibly reach a valid sum.
The algorithm examines all valid pointer states without missing or double-counting any triplet.
Therefore the final count is correct.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n^2) | Sorting plus two-pointer scans |
| Space | O(1) or O(log n) | Depends on sorting implementation |
The outer loop runs n times.
The inner two-pointer scan moves each pointer at most n steps total.
Implementation
class Solution:
def threeSumSmaller(self, nums: list[int], target: int) -> int:
nums.sort()
n = len(nums)
count = 0
for i in range(n - 2):
left = i + 1
right = n - 1
while left < right:
total = nums[i] + nums[left] + nums[right]
if total < target:
count += right - left
left += 1
else:
right -= 1
return countCode Explanation
We first sort the array:
nums.sort()Sorting enables the two-pointer logic.
For each first index:
for i in range(n - 2):we search for valid pairs to the right.
The pointers start here:
left = i + 1
right = n - 1Compute the current triplet sum:
total = nums[i] + nums[left] + nums[right]If the sum is valid:
if total < target:then every index between left and right also works.
So we count them all at once:
count += right - leftThen move left to search for more triplets.
If the sum is too large:
else:
right -= 1we decrease right to reduce the sum.
Finally return the total count.
Testing
def run_tests():
s = Solution()
assert s.threeSumSmaller([-2, 0, 1, 3], 2) == 2
assert s.threeSumSmaller([], 0) == 0
assert s.threeSumSmaller([0, 0, 0], 1) == 1
assert s.threeSumSmaller([1, 1, -2], 1) == 1
assert s.threeSumSmaller([3, 1, 0, -2], 4) == 3
assert s.threeSumSmaller([1, 2, 3, 4], 100) == 4
assert s.threeSumSmaller([5, 5, 5], 10) == 0
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
| Standard example | Basic two-pointer behavior |
| Empty array | No triplets |
| All zeros | Single valid triplet |
| Negative values | Mixed-sign handling |
| Unsorted input | Confirms sorting works |
| Very large target | All triplets valid |
| Impossible target | No valid triplets |