259. 3Sum Smaller


Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 <= i < j < k < n that satisfy the condition nums[i] + nums[j] + nums[k] < target.

For example, given nums = [-2, 0, 1, 3], and target = 2.

Return 2. Because there are two triplets which sums are less than 2:

[-2, 0, 1]
[-2, 0, 3]

Follow up:
Could you solve it in O(n2) runtime?


Solution


Approach #1 (Brute Force) [Time Limit Exceeded]

The brute force approach is to find every possible triplets subjected to and test for the condition.

Complexity analysis

  • Time complexity : . The total number of such triplets is , which is . Therefore, the time complexity of the brute force approach is .

  • Space complexity : .


Approach #2 (Binary Search) [Accepted]

Before we solve this problem, it is helpful to first solve this simpler twoSum version.

Given a array, find the number of index pairs , with that satisfy the condition

If we sort the array first, then we could apply binary search to find the largest index such that for each . Once we found that largest index , we know there must be pairs that satisfy the above condition with 's value fixed.

Finally, we can now apply the twoSum solution to threeSum directly by wrapping an outer for-loop around it.

public int threeSumSmaller(int[] nums, int target) {
    Arrays.sort(nums);
    int sum = 0;
    for (int i = 0; i < nums.length - 2; i++) {
        sum += twoSumSmaller(nums, i + 1, target - nums[i]);
    }
    return sum;
}

private int twoSumSmaller(int[] nums, int startIndex, int target) {
    int sum = 0;
    for (int i = startIndex; i < nums.length - 1; i++) {
        int j = binarySearch(nums, i, target - nums[i]);
        sum += j - i;
    }
    return sum;
}

private int binarySearch(int[] nums, int startIndex, int target) {
    int left = startIndex;
    int right = nums.length - 1;
    while (left < right) {
        int mid = (left + right + 1) / 2;
        if (nums[mid] < target) {
            left = mid;
        } else {
            right = mid - 1;
        }
    }
    return left;
}

Note that in the above binary search we choose the upper middle element instead of the lower middle element . The reason is due to the terminating condition when there are two elements left. If we chose the lower middle element and the condition evaluates to true, then the loop will never terminate. Choosing the upper middle element will guarantee termination.

Complexity analysis

  • Time complexity : . The binarySearch function takes time, therefore the twoSumSmaller takes time. The threeSumSmaller wraps with another for-loop, and therefore is time.

  • Space complexity : .


Approach #3 (Two Pointers) [Accepted]

Let us try sorting the array first. For example, becomes .

Let us look at an example , and .

[1, 2, 3, 5, 8]
            
left       right

Let us initialize two indices, and pointing to the first and last element respectively.

When we look at the sum of first and last element, it is , which is . That tells us no index pair will ever contain the index . So the next logical step is to move the right pointer one step to its left.

[1, 2, 3, 5, 8]
         
left    right

Now the pair sum is , which is . How many pairs with one of the that satisfy the condition? You can tell by the difference between and which is , namely and . Therefore, we move one step to its right.

public int threeSumSmaller(int[] nums, int target) {
    Arrays.sort(nums);
    int sum = 0;
    for (int i = 0; i < nums.length - 2; i++) {
        sum += twoSumSmaller(nums, i + 1, target - nums[i]);
    }
    return sum;
}

private int twoSumSmaller(int[] nums, int startIndex, int target) {
    int sum = 0;
    int left = startIndex;
    int right = nums.length - 1;
    while (left < right) {
        if (nums[left] + nums[right] < target) {
            sum += right - left;
            left++;
        } else {
            right--;
        }
    }
    return sum;
}

Complexity analysis

  • Time complexity : . The twoSumSmaller function takes time because both left and right traverse at most n steps. Therefore, the overall time complexity is .

  • Space complexity : .