如果假设输入一个数组 nums
和一个目标和 target
,请你返回 nums
中能够凑出 target
的两个元素的值,比如输入 nums = [5,3,1,6], target = 9
,那么算法返回两个元素 [3,6]
两数之和的核心步骤
lo为最左边的元素,hi为最右边的元素
有了上面的铺垫就来解决n数之和的问题了,比如一个三数之和你可以穷举一个数字然后调用二数之和,四数之和你可以穷举一个数字然后调用三数之和,那么N数之和呢?
总体就是n==2时的时候就用两数之和的解法,然后大的时候递归调用。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
return nSumTarget(nums,3,0,0);
}
private static List<Integer> transferArrayToList(int[] array){
List<Integer> transferedList = new ArrayList<>();
Arrays.stream(array).forEach(arr -> transferedList.add(arr));
return transferedList;
}
private static List<List<Integer>> nSumTarget(
int[] nums, int n, int start, int target) {
int sz = nums.length;
List<List<Integer>> res =new ArrayList<>();
// 至少是 2Sum,且数组大小不应该小于 n
if (n < 2 || sz < n) return res;
// 2Sum 是 base case
if (n == 2) {
// 双指针那一套操作
int lo = start, hi = sz - 1;
while (lo < hi) {
int sum = nums[lo] + nums[hi];
int left = nums[lo], right = nums[hi];
if (sum < target) {
while (lo < hi && nums[lo] == left) lo++;
} else if (sum > target) {
while (lo < hi && nums[hi] == right) hi--;
} else {
res.add(transferArrayToList(new int[]{nums[lo],nums[hi]}));
while (lo < hi && nums[lo] == left) lo++;
while (lo < hi && nums[hi] == right) hi--;
}
}
} else {
// n > 2 时,递归计算 (n-1)Sum 的结果
for (int i = start; i < sz; i++) {
List<List<Integer>>
sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
for (List<Integer> arr : sub) {
// (n-1)Sum 加上 nums[i] 就是 nSum
arr.add(nums[i]);
res.add(arr);
}
while (i < sz - 1 && nums[i] == nums[i + 1]) i++;
}
}
return res;
}
}
此方法可用的前提是数组必须是排序的
- 双指针那一块就不具体讲解了,
- else语句里的这一块总体的意思就是,穷举一个数递归调用(n-1 )sum,
List<List<Integer>> sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
这句话的意思就是固定住一个数之后对后面的数进行递归调用,此时对于(n-1)sum的target来说就是target-nums[i]了