三数之和
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
方法一:暴力解法
遍历选取任意三个元素相加是否为零,利用Set去重。
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> Set = new HashSet<List<Integer>>();
Arrays.sort(nums);
for(int i=0;i<nums.length-2;i++)
for(int j=i+1;j<nums.length-1;j++)
for(int k=j+1;k<nums.length;k++)
if(nums[i]+nums[j]+nums[k]==0)Set.add(Arrays.asList(nums[i],nums[j],nums[k]);
return new ArrayList<List<Integer>>(Set);
}
- 时 间 复 杂 度 : O ( n 3 ) 时间复杂度:O(n^{3}) 时间复杂度:O(n3)
- 空 间 复 杂 度 : O ( 1 ) 空间复杂度:O(1) 空间复杂度:O(1)
方法二:双指针解法
固定三个指针中最左(最小)数字的指针 i,双指针 j,k 分设在数组索引 (i+1, nums.length-1) 两端,通过双指针交替向中间移动,记录 nums[i] + nums[j] + nums[k] == 0 时的nums组合:
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> List = new ArrayList<List<Integer>>();
if(nums.length < 3) return List;
Arrays.sort(nums); // 进行排序
for (int i = 0; i < nums.length ; i++) {
if(nums[i] > 0) break; // nums[i]大于0,则三数之和大于0,所以结束跳出
if(i > 0 && nums[i] == nums[i-1]) continue; // 去掉重复
for(int j=i+1,k=nums.length-1;j < k;){
int He = nums[i] + nums[j] + nums[k];
if(He == 0){
List.add(Arrays.asList(nums[i],nums[j],nums[k]));
while (j<k && nums[j] == nums[j+1]) j++; // 去掉左重复
while (j<k && nums[k] == nums[k-1]) k--; // 去掉右重复
j++; k--;
}
else if (He < 0) j++;
else if (He > 0) k--;
}
}
return List;
}
- 时 间 复 杂 度 : O ( n 2 ) 时间复杂度:O(n^{2}) 时间复杂度:O(n2)
- 空 间 复 杂 度 : O ( 1 ) 空间复杂度:O(1) 空间复杂度:O(1)
最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
给定数组 nums = [-1,2,1,-4], 和 target = 1.
满足要求的三元组集合为:
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2)
方法一:双指针解法
与三数之和类似。三数之和要求找到三个元素之和为0的所有不重复情况,这里找到三个元素之和最接近目标值的情况(唯一)。首先对原数组排序,然后维护一个绝对差值变量和对应的结果变量。遍历数组,使用双指针j和k,根据三数之和与目标值的大小关系来移动指针,注意跳过重复情况,并记录与目标值的最小绝对差和对应的三数之和。
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int He,min;
if(nums.length<3)return target;
min=nums[0]+nums[1]+nums[2];
for(int i=0;i<nums.length-2;i++) {
if(i!=0) while (nums[i]==nums[i-1]&&i<nums.length-2) i++;
for(int j=i+1,k=nums.length-1;j<k;) {
He=nums[i]+nums[j]+nums[k];
if(He<target) {
if(target-He<Math.abs(min-target))min=He;
j++;
}
else if(He>target) {
if(He-target<Math.abs(min-target))min=He;
k--;
}
else return target;
}
}
return min;
}
- 时 间 复 杂 度 : O ( n 2 ) 时间复杂度:O(n^{2}) 时间复杂度:O(n2)
- 空 间 复 杂 度 : O ( 1 ) 空间复杂度:O(1) 空间复杂度:O(1)