15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
单层循环+双指针求解
代码:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
//当数组元素的个数小于三直接返回
if (nums.length < 3 || nums == null) {
return res;
}
//对数组先排序
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {//排好序之后,如果nums[i]大于0,后面的和不会等于0,直接返回结果
return res;
}
//因为排好序了,重复元素一定是挨着的
if(i>0 && nums[i]==nums[i-1]) continue;//第一个元素一定不会是重复的
int L = i + 1;
int R = nums.length - 1;
while (L < R) {
int r = nums[i] + nums[L] + nums[R];
if (r == 0) {
ArrayList<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[L]);
list.add(nums[R]);
res.add(list);
while(L<R && nums[L+1]==nums[L]) L++;//为了避免[-2,0,0,2,2]这种情况
while(L<R && nums[R-1]==nums[R]) R--;
//别忘了移动坐标
L++;
R--;
} else if (r > 0) {//说明R太大,向左移
R--;
} else if (r < 0) {//说明L太小,向右移
L++;
}
}
}
return res;
}
}
18. 四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
双层循环+双指针
代码:
解法一:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res=new ArrayList<>();
if(nums.length < 4 || nums==null){
return res;
}
//给数组排序
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
int L=j+1;
int R=nums.length-1;
while (L<R){
int sum=nums[i]+nums[j]+nums[L]+nums[R];
if(sum==target){
ArrayList<Integer> list=new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[L]);
list.add(nums[R]);
if(!res.contains(list)){//为了避免[2,2,2,2,2]这种情况
res.add(list);
}
//找到目标之后,别忘移动坐标
L++;
R--;
}else if(sum>target){
R--;
}else {
L++;
}
}
}
}
return res;
}
}
解法二:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res=new ArrayList<>();
if(nums.length < 4 || nums==null){
return res;
}
//给数组排序
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
if(i>0 && nums[i-1]==nums[i]) continue; //为了避免[2,2,2,2,2]
for(int j=i+1;j<nums.length;j++){
if(j>i+1 && nums[j-1]==nums[j]) continue; //为了避免[2,2,2,2,2]
int L=j+1;
int R=nums.length-1;
while (L<R){
int sum=nums[i]+nums[j]+nums[L]+nums[R];
if(sum==target){
ArrayList<Integer> list=new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[L]);
list.add(nums[R]);
//if(!res.contains(list)){//为了避免[2,2,2,2,2]
res.add(list);
// }
//去重,避免res中存在重复集合
while (L<R && nums[L+1]==nums[L]) L++;
while (L<R && nums[R]==nums[R-1]) R--;
//找到目标之后,别忘移动坐标
L++;
R--;
}else if(sum>target){
R--;
}else {
L++;
}
}
}
}
return res;
}
}
总结
不管是几个数求和,使用双指针+剩余要确定的数就是循环嵌套数,例如四数求和,双指针可以确定两个数,还有两个数用双层循环嵌套确定。
唯一需要注意的是去重,结果集里面的集合不能重复,可以使用res.contains(list)进行判定,但这样效率没有直接跳过重复元素效率高。例如解法一与解法二的对比