LeetCode 259 - 3Sum Smaller
The problem asks us to count how many distinct index triplets (i, j, k) satisfy two conditions: 1. The indices are ordered such that 0 <= i < j < k < n 2.
Difficulty: 🟡 Medium
Topics: Array, Two Pointers, Binary Search, Sorting
Solution
Problem Understanding
The problem asks us to count how many distinct index triplets (i, j, k) satisfy two conditions:
- The indices are ordered such that
0 <= i < j < k < n - The sum of the corresponding values is strictly smaller than the given
target
In other words, we must examine combinations of three different elements in the array and determine how many of those combinations produce a sum less than target.
The input consists of:
- An integer array
nums - An integer
target
The output is a single integer representing the number of valid triplets.
A key detail is that we are counting triplets by index positions, not by unique values. If the same number appears multiple times in different positions, each valid index combination counts separately.
The constraints are important for deciding which algorithm is practical:
ncan be as large as3500- A brute-force solution would require checking all possible triplets
- The number of triplets in the worst case is approximately
n^3 / 6
For n = 3500, a cubic algorithm becomes far too slow. We therefore need something more efficient than O(n^3).
The array may also contain:
- Negative numbers
- Positive numbers
- Duplicate values
- Empty input
The problem guarantees that the final answer fits within a 32-bit integer range, so we do not need special overflow handling.
Several edge cases are important:
- Arrays with fewer than three elements cannot form any triplets
- Duplicate numbers should still count as distinct index combinations
- Negative values can produce many valid triplets
- Already sorted or reverse-sorted arrays should still work correctly
- Large numbers of valid combinations must be counted efficiently without explicitly enumerating every triplet
Approaches
Brute Force Approach
The most direct solution is to try every possible triplet.
We can use three nested loops:
- The first loop chooses index
i - The second loop chooses index
j - The third loop chooses index
k
For every triplet, we compute:
nums[i] + nums[j] + nums[k]
If the sum is smaller than target, we increment the answer.
This approach is guaranteed to be correct because it explicitly checks every possible valid triplet. Nothing is skipped.
However, the time complexity is O(n^3), which is too slow when n can be 3500.
Optimal Approach, Sorting + Two Pointers
The key observation is that after sorting the array, we can use ordering properties to count many triplets at once.
Suppose we fix the first element nums[i].
We then want to find pairs (j, k) such that:
nums[i] + nums[j] + nums[k] < target
Because the array is sorted:
- If a particular pair
(j, k)works, - Then every index between
jandkalso works withj
For example, if:
nums[i] + nums[left] + nums[right] < target
then:
nums[i] + nums[left] + nums[right - 1] < target
nums[i] + nums[left] + nums[right - 2] < target
...
This means we can count multiple triplets in one step instead of checking them individually.
We use two pointers:
leftstarts just afterirightstarts at the end of the array
Depending on the current sum:
- If the sum is smaller than target, all pairs from
leftthroughrightare valid - Otherwise, we decrease the sum by moving
rightleftward
This reduces the complexity from O(n^3) to O(n^2).
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n³) | O(1) | Checks every possible triplet explicitly |
| Optimal | O(n²) | O(1) or O(log n) depending on sort implementation | Uses sorting and two pointers to count multiple triplets efficiently |
Algorithm Walkthrough
Optimal Algorithm
- Sort the array in non-decreasing order.
Sorting is essential because the two-pointer strategy depends on the array being ordered. Once sorted, increasing or decreasing pointers predictably changes the sum.
2. Initialize a variable count = 0.
This variable stores the total number of valid triplets.
3. Iterate through the array using index i from 0 to n - 3.
We treat nums[i] as the first element of the triplet.
4. For each i, initialize two pointers:
left = i + 1right = n - 1
These pointers search for valid second and third elements.
5. While left < right, compute the current sum:
current_sum = nums[i] + nums[left] + nums[right]
- If
current_sum < target, then every index betweenleftandrightforms a valid triplet withi.
Since the array is sorted:
nums[right] >= nums[right - 1] >= ...
Replacing right with any smaller index only decreases the sum.
Therefore, the number of valid triplets is:
right - left
Add this amount to count.
- Move
leftone step to the right.
We have already counted all triplets beginning with the current left, so we continue searching for new combinations.
2. Otherwise, if the sum is too large, move right one step left.
This decreases the sum and may create valid triplets.
3. Continue until all possibilities are processed.
4. Return count.
Why it works
The correctness relies on the sorted order of the array.
When:
nums[i] + nums[left] + nums[right] < target
every index between left and right also works because replacing nums[right] with a smaller element cannot increase the sum.
This allows us to count right - left triplets at once without missing or double-counting any combinations.
Every valid triplet is eventually considered exactly once because:
iuniquely fixes the first element- The two-pointer scan systematically explores all valid
(left, right)pairs
Python Solution
from typing import List
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:
current_sum = nums[i] + nums[left] + nums[right]
if current_sum < target:
count += right - left
left += 1
else:
right -= 1
return count
The implementation begins by sorting the array. This enables the two-pointer optimization that follows.
The outer loop selects the first element of each triplet. Since we need three elements total, the loop only runs until n - 3.
For each fixed index i, two pointers search the remaining portion of the array.
The variable current_sum stores the sum of the current triplet candidate.
When the sum is smaller than the target, all positions between left and right are guaranteed to work because the array is sorted. Instead of checking each individually, the code adds right - left directly to the answer.
If the sum is too large, the only way to reduce it is to move right leftward.
The algorithm efficiently counts all valid triplets without enumerating every combination explicitly.
Go Solution
package main
import "sort"
func threeSumSmaller(nums []int, target int) int {
sort.Ints(nums)
n := len(nums)
count := 0
for i := 0; i < n-2; i++ {
left := i + 1
right := n - 1
for left < right {
currentSum := nums[i] + nums[left] + nums[right]
if currentSum < target {
count += right - left
left++
} else {
right--
}
}
}
return count
}
The Go implementation closely mirrors the Python solution.
The main difference is that Go uses sort.Ints(nums) from the standard library to sort the slice in place.
Go slices naturally handle empty arrays, so no additional checks are required for edge cases such as arrays with fewer than three elements.
Integer overflow is not a concern because the constraints are small. The largest possible triplet sum is well within Go's int range.
Worked Examples
Example 1
Input:
nums = [-2,0,1,3]
target = 2
After sorting:
[-2,0,1,3]
Iteration with i = 0
| left | right | Triplet | Sum | Action | Count |
|---|---|---|---|---|---|
| 1 | 3 | (-2,0,3) | 1 | Valid, add 2 | 2 |
| 2 | 3 | (-2,1,3) | 2 | Too large, move right | 2 |
Why add 2?
Because when (-2,0,3) works:
(-2,0,1)also works
So both triplets are counted together.
Iteration with i = 1
| left | right | Triplet | Sum | Action | Count |
|---|---|---|---|---|---|
| 2 | 3 | (0,1,3) | 4 | Too large | 2 |
Final answer:
2
Example 2
Input:
nums = []
target = 0
The array length is less than 3, so the outer loop never runs.
Result:
0
Example 3
Input:
nums = [0]
target = 0
Again, fewer than three elements exist.
Result:
0
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n²) | Sorting takes O(n log n), two-pointer scanning dominates with O(n²) |
| Space | O(1) or O(log n) | Only constant extra variables are used, excluding sorting internals |
The outer loop runs n times, and for each iteration the two pointers move across the array at most once. Since each pointer only moves inward, the total work per iteration is linear.
This produces an overall time complexity of O(n²).
The algorithm itself uses only a few variables. Depending on the language and sorting implementation, sorting may require logarithmic stack space internally.
Test Cases
from typing import List
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:
current_sum = nums[i] + nums[left] + nums[right]
if current_sum < target:
count += right - left
left += 1
else:
right -= 1
return count
solution = Solution()
assert solution.threeSumSmaller([-2, 0, 1, 3], 2) == 2 # provided example
assert solution.threeSumSmaller([], 0) == 0 # empty array
assert solution.threeSumSmaller([0], 0) == 0 # single element
assert solution.threeSumSmaller([1, 1], 3) == 0 # fewer than 3 elements
assert solution.threeSumSmaller([0, 0, 0], 1) == 1 # exactly one valid triplet
assert solution.threeSumSmaller([3, 1, 0, -2], 4) == 3 # unsorted input
assert solution.threeSumSmaller([-1, -1, -1, -1], 0) == 4 # all triplets valid
assert solution.threeSumSmaller([5, 5, 5], 10) == 0 # no valid triplets
assert solution.threeSumSmaller([-2, 0, 1, 3], 5) == 4 # all triplets valid
assert solution.threeSumSmaller([1, 2, 3, 4, 5], 8) == 2 # selective valid triplets
assert solution.threeSumSmaller([-5, -4, -3, -2, -1], -6) == 10 # all negative numbers
Test Summary
| Test | Why |
|---|---|
[-2,0,1,3], target=2 |
Verifies the main example |
[] |
Ensures empty input returns zero |
[0] |
Ensures insufficient length is handled |
[1,1] |
Confirms arrays with fewer than three elements return zero |
[0,0,0], target=1 |
Tests exactly one valid triplet |
[3,1,0,-2], target=4 |
Verifies sorting is correctly used |
[-1,-1,-1,-1], target=0 |
Tests duplicates and multiple valid combinations |
[5,5,5], target=10 |
Tests case with no valid triplets |
[-2,0,1,3], target=5 |
Tests case where all triplets are valid |
[1,2,3,4,5], target=8 |
Tests mixed valid and invalid triplets |
[-5,-4,-3,-2,-1], target=-6 |
Tests all-negative arrays |
Edge Cases
Arrays With Fewer Than Three Elements
If the array contains fewer than three numbers, no triplet can exist. This is a common source of off-by-one bugs in nested loop solutions.
The implementation handles this naturally because the outer loop:
for i in range(n - 2):
does not execute when n < 3.
Duplicate Values
Arrays may contain repeated numbers, and the problem counts triplets by index positions rather than distinct values.
For example:
[-1, -1, -1, -1]
contains multiple valid triplets even though the values repeat.
The implementation correctly counts every distinct index combination because it never skips duplicates.
All Triplets Valid
In some inputs, nearly every possible triplet satisfies the condition.
Example:
nums = [-5,-4,-3,-2,-1]
target = 100
A naive solution still checks every triplet individually.
The optimized solution handles this efficiently by counting multiple triplets at once using:
count += right - left
This prevents unnecessary repeated work and preserves quadratic complexity.
No Valid Triplets
Some arrays produce no valid triplets at all.
Example:
nums = [10,10,10]
target = 5
The implementation correctly returns zero because every computed sum exceeds the target, causing the right pointer to move inward until the search terminates.