给定一个长度为 n
的整数数组和一个目标值 target
,寻找能够使条件 nums[i] + nums[j] + nums[k] < target
成立的三元组 i, j, k
个数(0 <= i < j < k < n
)。
示例 1:
输入: nums = [-2,0,1,3], target = 2 输出: 2 解释: 因为一共有两个三元组满足累加和小于 2: [-2,0,1] [-2,0,3]
示例 2:
输入: nums = [], target = 0 输出: 0
示例 3:
输入: nums = [0], target = 0 输出: 0
提示:
n == nums.length
0 <= n <= 3500
-100 <= nums[i] <= 100
-100 <= target <= 100
解法: 双指针 + 两数之和
当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从 O(N^2) 减少至 O(N)。为什么是 O(N) 呢?这是因为在枚举的过程每一步中,「左指针」会向右移动一个位置(也就是题目中的 j),而「右指针」会向左移动若干个位置,这个与数组的元素有关,但我们知道它一共会移动的位置数为 O(N),均摊下来,每次也向左移动一个位置,因此时间复杂度为 O(N)。
注意到我们的代码中还有第一重循环,时间复杂度为 O(N),因此枚举的总时间复杂度为 O(N^2)。由于排序的时间复杂度为 O(NlogN),在渐进意义下小于前者,因此算法的总时间复杂度为 O(N^2)。
类似题型:
LeetCode 1099. 小于 K 的两数之和-CSDN博客
class Solution {
public int threeSumSmaller(int[] nums, int target) {
int ans = 0;
int n = nums.length;
if (n < 3) {
return 0;
}
Arrays.sort(nums);
for (int i = 0; i < n - 2; i++) {
// nums[j] + nums[k] < target - nums[i]
// 数组是排好序的,固定i,则枚举j的过程中,符合要求的k一定是逐渐减小的
int k = n - 1;
for (int j = i + 1; j < n - 1; j++) {
// nums[k] < target - nums[i] - nums[j]
while (k > j && nums[i] + nums[j] + nums[k] >= target) {
k--;
}
if (k == j) {
break;
}
// 由于数组是排好序的,此时固定i,j,k是满足要求的最大值
// 此时 (j,k]之间的数目是满足要求的k的数目
ans += k - j;
}
}
return ans;
}
}
复杂度分析
- 时间复杂度:O(N ^ 2),N 是数组nums 的长度。数组排序的时间复杂度是O(N logN)。
- 空间复杂度:O(1)。