【LeetCode每日一题】[中等]18. 四数之和
18. 四数之和
题目来源
算法思想:数组,回溯
题目:
指针法思路:
- 首先将数组进行排序
- 运用指针,nums[i] and nums[j] 是四数和中的前两位数,
- 剩下两位在区间中[j+1,n-1]中得到,
- 运用下面的技巧进行剪枝:
java代码–指针法
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> quadruplets = new ArrayList<List<Integer>>();
if (nums == null || nums.length < 4) {
return quadruplets;
}
Arrays.sort(nums);//排序
int length = nums.length;
for (int i = 0; i < length - 3; i++) {
//i,确定第一个数值
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}//便于去重,同一个位置的数,不使用相同的数值
if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}//如果当前连续4个数比target大,则说明之后不可能存在答案,剪枝
if (nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}//如果当前的值加上最大的三个数小于target,则当前数不可能凑成四叔和,舍弃,剪枝,后续可能存在答案,所以是continue
//确定第二个数值
for (int j = i + 1; j < length - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}//便于去重,同一个位置的数,不使用相同的数值
if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break;
}//如果当前连续4个数比target大,则说明之后不可能存在答案,剪枝
if (nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
continue;
}//如果当前的值i,j加上最大的两个数小于target,则当前数不可能凑成四叔和,舍弃,后续可能存在答案,所以是continue,继续寻找合适的j,第二个数
//找到了合适的前两个数i,j,现在确定剩下的两个数,在区间[j+1,n-1]中
int left = j + 1, right = length - 1;//区间左右边界
while (left < right) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
quadruplets.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));//如果是,将答案放入其中
while (left < right && nums[left] == nums[left + 1]) {
left++;
}//如果有左边界值相同,则跳过,去重,一层循环中,同一个位置不放入相同的数值
left++;//左边界,后移
while (left < right && nums[right] == nums[right - 1]) {
right--;
}//如果有右边界值相同,则跳过,去重,一层循环中,同一个位置不放入相同的数值
right--;//有边界,前移
} else if (sum < target) {
left++;//如果sum<target,则说明,当前总和太小,左边界后移
} else {
right--;//如果sum>target,则说明,当前总和太大,右边界前移
}
}
}
}
return quadruplets;
}
}
java代码–回溯
分析思路.
利用回溯全排列,剪枝去重,核心剪枝方法如下:
if(start != i && nums[i - 1] == nums[i]) {
continue;
}
//当前位置的第二次循环,如果nums[i - 1] == nums[i],则nums[i]已经在之前向下递归尝试过了,舍弃,去重
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
dfs(nums, target, 4, 0);
return res;
}
private void dfs(int[] nums, int target, int falg, int start) {
if (falg == 0 && target == sum) {
res.add(new ArrayList<Integer>(list));
return;
}
for (int i = start; i < nums.length; i++) {
if(nums.length - i < 4 - list.size()) {
return;
}//剩余的数量,不够凑成4,直接return
if(start != i && nums[i - 1] == nums[i]) {
continue;
}//当前位置的第二次循环,如果nums[i - 1] == nums[i],则nums[i]已经在之前向下递归尝试过了,舍弃,去重
if(i < nums.length - 1 && sum + nums[i] + (3 - list.size()) * nums[i + 1] > target) {
return;
}//当下可能,最小的数值加起来大于目标值,剩余数组中不可能含有目标值,直接返回
if(i < nums.length - 1 && sum + nums[i] + (3 - list.size()) * nums[nums.length - 1] < target) {
continue;
}//当前可能,最大的数值加起来小于目标值,当前数值不用往下递归,直接遍历尝试下一个元素
list.add(nums[i]);
sum += nums[i];
dfs(nums, target, falg-1, i+1);
list.remove(list.size()-1);
sum -= nums[i];
}
}
}