这是我参与更文挑战的第15天,活动详情查看: 更文挑战
前言
力扣第十八题 四数之和 如下所示:
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [], target = 0
输出:[]
一、思路
写过力扣第十五题-三数之和都知道,题目与此题基本类似,不过是将四数换成了三数。三数之和中使用的是排序 + 双指针 来简化算法的复杂度,使之变成 O(n^2)。我刚开始也以为这一题目会有什么别的解法,一看题解任然是 排序 + 双指针 ,等于是多加了一层循环,复杂度变为了 O(n^3)。
说一下这一题怎么来实现:
- 先将数组以升序排列(这样可以防止结果重复)
- 前三个元素从左边选择,第四个元素从右边选即可
二、实现
与三数之和一样,第四个元素还是从最右边开始。如果最大的值和前三个元素相加都小于0,则可以跳过此次循环。
(比如前三个元素为 -4、-2、0,最大值为 5,此时就说明这前三个元素无论组合哪个元素都不会满足条件)
代码实现
tips:一定要进行合理的剪枝,不然时间复杂度会很高!
合理剪枝,重要的事情再说一遍!在实现代码中的前两次循环都会判断前四个元素和当前元素加后三个元素,来进行相应的跳出或继续下一次循环 。
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> ret = new ArrayList<>();
// 特殊情况
if (nums == null || nums.length < 4)
return ret;
// 给数组排序
Arrays.parallelSort(nums);
int len = nums.length;
// 复杂度O(n^3)
for (int i=0; i<nums.length-3; i++) {
if (i != 0 && nums[i] == nums[i-1])
continue;
// 如果前四个元素 > target,则跳出循环
if(nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target)
break;
// 如果当前元素加上最大的三个数 < target,则跳过此处循环
if (nums[i] + nums[len -3] + nums[len -2] + nums[len -1] < target)
continue;
for (int j=i+1; j<nums.length-2; j++) {
if (j != i+1 && nums[j] == nums[j-1])
continue;
// 如果前四个元素 > target,则跳出循环
if(nums[i] + nums[j] + nums[j+1] + nums[j+2] > target)
break;
// 如果当前元素加上最大的三个数 < target,则跳过此处循环
if (nums[i] + nums[j] + nums[len -2] + nums[len -1] < target)
continue;
// 第四重循环对应的指针
int m = nums.length-1;
for (int k=j+1; k<nums.length-1; k++) {
if (k != j+1 && nums[k] == nums[k-1])
continue;
// 向左移动指针,且右指针在左指针的右边
while(k < m && nums[i]+nums[j]+nums[k]+nums[m] > target) {
m = m-1;
}
if (k == m) {
break;
}
if (nums[i]+nums[j]+nums[k]+nums[m] == target) {
ret.add(Arrays.asList(nums[i], nums[j], nums[k], nums[m]));
}
}
}
}
return ret;
}
测试代码
public static void main(String[] args) {
int[] nums = {1,0,-1,0,-2,2};
new Number18().fourSum(nums, 1);
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥