1 数组排序+双指针+递归(Java实现)
/*
方法:数组排序 + 双指针 + 递归调用
难度:中等
思路:从二数之和扩展到三数之和,再扩展到四数之和
对排序后的数组进行遍历时(第1个数),第2、3、4个数调用已有的三数之和API
遍历过程需保证第1个数不能出现重复(第2、3、4个数已控制不会出现重复结果)
时间复杂度:O(n^3)
数组排序O(n logn)
计算三数之和的双指针操作O(n * n) → 循环调用n次
总时间复杂度:O(n logn + O(n* n) * n) = O(n^3)
空间复杂度:O(n)
类似题目:t001、t015
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
//对数组进行排序
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
//for循环遍历数组
for(int i = 0; i < nums.length; i++){
//调用三元组的函数threeSumTarget → 计算目标值为target - nums[i]的三元组
List<List<Integer>> temps = threeSumTarget(nums, i + 1,target - nums[i]);
if(!temps.isEmpty()){
for(List<Integer> temp : temps){
temp.add(nums[i]);
res.add(temp);
}
//跳过左侧遍历过程中的重复元素
while(i < nums.length - 1 && nums[i] == nums[i+1]) i++;
}
}
return res;
}
//返回数组nums中和为target的三元组(循环的起始位置为start)
public List<List<Integer>> threeSumTarget(int[] nums, int start , int target){
//对数组进行排序
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
//for循环遍历数组
for(int i = start; i < nums.length; i++){ //第2个数的起始索引为start
//调用二元组的函数twoSumTarget → 计算目标值为target - nums[i]的二元组
List<List<Integer>> temps = twoSumTarget(nums, i + 1,target - nums[i]);
if(!temps.isEmpty()){
for(List<Integer> temp : temps){
temp.add(nums[i]);
res.add(temp);
}
//跳过左侧遍历过程中的重复元素
while(i < nums.length - 1 && nums[i] == nums[i+1]) i++;
}
}
return res;
}
//返回数组nums中和为target的二元组(循环的起始位置为start)
public List<List<Integer>> twoSumTarget(int[] nums, int start ,int target){
//对数组进行排序
Arrays.sort(nums);
int low = start; //第2个元素的起始索引位置start
int high = nums.length - 1;
List<List<Integer>> res = new ArrayList<>();
//while循环遍历数组
while(low < high){
//记录当前的左值和右值
int left = nums[low];
int right = nums[high];
int sum = left + right;
if(sum < target){
while(low < high && nums[low] == left) low++; //跳过左侧所有重复的元素
}else if(sum > target){
while(low < high && nums[high] == right) high--; //跳过右侧所有重复的元素
}else if(sum == target){
//向结果集res中添加符合条件的结果
res.add(new ArrayList<Integer>(Arrays.asList(left, right)));
while(low < high && nums[low] == left) low++; //跳过左侧所有重复的元素
while(low < high && nums[high] == right) high--; //跳过右侧所有重复的元素
}
}
return res;
}
}
2 拓展:N数之和
/*
框架:求解数组nums中N个数之和为目标值,结果不能重复
方法:数组排序 + 双指针 + 递归调用
思路:从两数之和、三数之和及四数之和中找出规律
(1)两数之和:while循环,左右指针
(2)三数之和:for循环,控制两数之和while循环的起始索引位置,调用两数之和结果
(3)N数之和:for循环,控制for循环的起始索引,避免第1个数出现重复;递归调用 N - 1数之和的结果
注意:调用前必须对数组进行排序,避免每次递归调用过程中进行没必要的排序
类似题目:t001、t015、t018
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
//对数组nums进行排序
Arrays.sort(nums);
// n 为 4,从 nums[0] 开始计算和为 target 的四元组
return nSumTarget(nums, 4 , 0, target);
}
//调用前必须对数组进行排序,避免每次递归调用过程中进行没必要的排序
public List<List<Integer>> nSumTarget(int[] nums, int n, int start ,int target) {
List<List<Integer>> res = new ArrayList<>();
//边界排除:至少是2个数求和,且不能超过数组元素个数
if(n < 2 || n > nums.length) return res;
/* 两数之和 */
if(n == 2){
int low = start; //起始索引位置start
int high = nums.length - 1;
//while循环遍历数组
while(low < high){
//记录当前的左值和右值
int left = nums[low];
int right = nums[high];
int sum = left + right;
if(sum < target){
while(low < high && nums[low] == left) low++; //跳过左侧所有重复的元素
}else if(sum > target){
while(low < high && nums[high] == right) high--; //跳过右侧所有重复的元素
}else if(sum == target){
//向结果集res中添加符合条件的结果
res.add(new ArrayList<Integer>(Arrays.asList(left, right)));
while(low < high && nums[low] == left) low++; //跳过左侧所有重复的元素
while(low < high && nums[high] == right) high--; //跳过右侧所有重复的元素
}
}
}else{ /* 多数之和 */
//递归计算 (n-1)Sum 的结果
//for循环遍历数组
for(int i = start; i < nums.length; i++){
//调用 (n-1)元组 的函数threeSumTarget → 计算目标值为target - nums[i]的(n-1)元组
//递归调用 N - 1数之和的结果
List<List<Integer>> temps = nSumTarget(nums, n - 1,i + 1,target - nums[i]);
if(!temps.isEmpty()){
for(List<Integer> temp : temps){
temp.add(nums[i]);
res.add(temp);
}
//跳过左侧遍历过程中的重复元素
while(i < nums.length - 1 && nums[i] == nums[i+1]) i++;
}
}
}
return res;
}
}