Leetcode | 15. 三数之和
题目
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
解题
排序+双指针
由两数之和的双指针解法引申而来。
三个数的双指针这样设定:
- 首先将数组排序,以便之后双指针的移动;
- 然后使用一层循环固定左边界 first 从0到n-3(第一个数);这样就变成了剩下范围内的两数之和问题:另外的两个数由双指针second和third分别从i+1到n-1的两头开始进行双指针的移动。
- 移动规则如下:sum = nums[first]+nums[second]+nums[third]
① 如果 sum>target,r左移,使总和更小更接近target(左移后还与原来重复的数字跳过)
② 如果 sum<target,l右移,使总和更大更接近target(右移后还与原来重复的数字跳过)
③ 如果 sum == target,返回结果。
此题中target = 0。
代码参考 力扣官方题解
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//排序+双指针
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<List<Integer>>();
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
List<Integer> list = new ArrayList<Integer>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
res.add(list);
}
}
}
return res;
}
}
排序+哈希
由两数之和的哈希解法引申而来。
因为给定目标和,求目标和为target的三个数,所以可以遍历时利用哈希表来进行求解,但是效率不高。
同样先排序+一重循环固定左边界,问题就变成了从左边界到最右边之间的两数之和的求解问题,这里采用两数之和的哈希求解法。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
//判断特殊情况
if(nums.length<3) return res;
// if(nums.length == 3){
// if(nums[0]+nums[1]+nums[2] == 0){
// List<Integer> tmp = new ArrayList<>();
// tmp.add(nums[0]);
// tmp.add(nums[1]);
// tmp.add(nums[2]);
// res.add(tmp);
// }
// return res;
// }
Arrays.sort(nums);
for(int i = 0; i < nums.length;i++){
if(nums[i]>0) break;
if(i>=1 && nums[i] == nums[i-1]){
continue;
}
if(nums[i] == 0){
if(i+2<nums.length && nums[i+1] == 0 && nums[i+2] == 0){
List<Integer> tmp = new ArrayList<>();
tmp.add(0);
tmp.add(0);
tmp.add(0);
res.add(tmp);
}
continue;
}
if(nums[i]<0){
Set<Integer> numset = new HashSet<>();
Set<Integer> s = new HashSet<>();
for(int j = i+1; j < nums.length; j++){
if(!s.contains(nums[j]) && numset.contains(0-nums[i]-nums[j])){
List<Integer> tmp = new ArrayList<>();
tmp.add(nums[i]);
tmp.add(nums[j]);
tmp.add(0-nums[i]-nums[j]);
res.add(tmp);
s.add(nums[j]);
}else{
numset.add(nums[j]);
}
}
}
}
return res;
}
}
Leetcode | 16. 最接近的三数之和
题目
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum-closest
解题
思路(排序+双指针)
同上,由两数之和的双指针解法引申而来。
排序后,先固定左边界(一重循环第一个数 i),再在剩下的范围内(i+1到n-1)解决两数之和的问题。
因为此题与之前求和问题不同的是,这题里可能含有不恰好等于target的情况,所以要求的是最接近于target的情况,就不能用哈希查找的方式了,只能采用双指针方法枚举各个情况逼近target,每种情况都用差值dif = Math.abs(sum-target)来判断,寻找差值最小的sum即为所求结果。
详解见注释。
Java实现
class Solution {
public int threeSumClosest(int[] nums, int target) {
//排序+双指针
Arrays.sort(nums);
int res = nums[0]+nums[1]+nums[2];
int dif = Math.abs(res-target);//当前和与目标的差值,求差值最小的res
if(nums.length==3) return res;
//一重循环代表第一个数i,双指针从i+1到n-1两头出发,分别代表第二和第三个数
for(int i = 0; i <= nums.length-3;i++){
//如果当前数和上一个数一样的话,可以不用再枚举一遍,之后的所有情况在上一轮已经枚举过了
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int l = i+1 , r = nums.length-1;
while(l<r){
if(nums[i]+nums[l]+nums[r]==target){
return res = target;
}
if(Math.abs(nums[i]+nums[l]+nums[r]-target)<dif){
res = nums[i]+nums[l]+nums[r];
dif = Math.abs(res-target);
}
if(nums[i]+nums[l]+nums[r]>target){
int r0 = r-1;
//r左移的过程中将一样的r值跳过,道理同上
while(l<r0 && nums[r0] == nums[r]){
r0--;
}
r = r0;
}else{
int l0 = l+1;
while(l0<r && nums[l0] == nums[l]){
l0++;
}
l = l0;
}
}
}
return res;
}
}