力扣18(java)-四数之和(中等)
题目:
给你一个由 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
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
参考官方题解
排序+双指针:先给数组nums进行升序排序,两个for循环确定前两个数,然后使用双指针确定后两个数,需要考虑以下几种情况进行剪枝:
- 在确定第一个数之后,如果nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target ,代表第一个数与之邻进的三个最小数之和都大于目标值了,则说明后面剩下的三个数无论取什么值,四数之和一定大于target,则需要退出第一轮循环;
- 在确定第一个数之后,如果nums[i] + nums[n-3] + nums[n-2] + nums[n-1] < target ,代表第一个数与数组中的三个最大数之和都小于目标值了,则说明后面剩下的三个数无论取什么值,四数之和一定小于target,则需要进入下一轮循环,枚举nums[i+1];
- 在确定前两个数之后,如果nums[i] + nums[j] + nums[j+1] + nums[j+2] > target ,说明剩下的两个数,无论取什么值,四个数之和一定会大于taret,因此退出第二层循环;
- 在确定前两个数之后,如果nums[i] + nums[j] + nums[n-2] + nums[n-1] < target ,说明剩下的两个数,无论取什么值,四个数之和一定会小于taret,因此进入下一轮,枚举nums[j+1];
使用双指针:左右指针分别指向下标 j+1和下标 n-1。每次计算四个数的和sum,并进行如下操作:
如果和 sum == target,则将枚举到的四个数加到答案中,然后将左指针右移直到遇到不同的数,将右指针左移直到遇到不同的数;
如果和sum < target,则将左指针右移一位;
如果和sum > target,则将右指针左移一位。
java代码:
1 class Solution { 2 public List<List<Integer>> fourSum(int[] nums, int target) { 3 List<List<Integer>> res = new ArrayList<List<Integer>>(); 4 int n = nums.length; 5 //如果数组的长度小于4 6 if(n < 4) return res; 7 //对数组进行排序 8 Arrays.sort(nums); 9 //第一个数,只能遍历到倒数第4位 10 for(int i = 0; i < n-3; i++){ 11 //:先去掉重复值 12 if(i > 0 && nums[i] == nums[i-1]) continue; 13 //如果邻近的四个数大于target,则退出 14 if((long)nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) break; 15 //如果与最大的三个数相加小于target,则说明nums[i]小了,需要进入新一轮循环 16 if((long)nums[i] + nums[n-3] + nums[n-2] + nums[n-1] < target) continue; 17 //确定第二个数 18 for(int j = i+1; j < n-2; j++){ 19 //去重 20 if(j > i + 1 && nums[j] == nums[j - 1]) continue; 21 //如果和j邻近的两个数和前两个相加之和大于target,则退出 22 if((long)nums[i] + nums[j] + nums[j+1] + nums[j+2] > target) break; 23 //如果与最大的三个数相加小于target,则说明nums[i]小了,需要进入新一轮循环 24 if((long)nums[i] + nums[j] + nums[n-2] + nums[n-1] < target) continue; 25 //确定了两个数之后,后两个数使用双指针 26 int L = j + 1; 27 int R = n - 1; 28 while(L < R){ 29 int sum = nums[i] + nums[j] + nums[L] + nums[R]; 30 if(sum == target){ 31 res.add(Arrays.asList(nums[i], nums[j], nums[L], nums[R])); 32 //跳过重复数 33 while(L < R && nums[L] == nums[L + 1]) L++; 34 while(L < R && nums[R] == nums[R - 1]) R--; 35 L++; 36 R--; 37 }else if(sum < target){ 38 L++; 39 }else{ 40 R--; 41 } 42 } 43 } 44 } 45 return res; 46 } 47 }
2023-05-11补充:
代码随想录思路: 参考代码随想录文字讲解 && 代码随想录视频讲解
细节:
①剪枝优化:如果还是只单纯比较nums[i] > target就剪枝,例如:[-4, -1, 0, 0] target = -5,nums[i] = -4 > t-5,剪枝的话后面就会错过答案所有应该加条件变成:nums[i] > target && nums[i] > 0,因为如果这时nums[i] 已经大于target了,nums[i]大于0,i 后面的数都大于0,越加越大,是不会等于target的这时候剪枝就正好。
②由于多了一层循环,故需要多一次剪枝和去重。
1 class Solution { 2 public List<List<Integer>> fourSum(int[] nums, int target) { 3 List<List<Integer>> ans = new ArrayList<>(); 4 Arrays.sort(nums); 5 if (nums.length < 4) return ans; 6 for (int i = 0; i < nums.length; i++){ 7 //剪枝,如果nums[i] > 0, nums[i] > target后面相加会越来越大 8 if (nums[i] > target && nums[i] > 0) break; 9 //第一层去重 10 if (i > 0 && nums[i] == nums[i-1]) continue; 11 for (int k = i + 1; k < nums.length; k++){ 12 //第二层剪枝 13 int sum1 = nums[i] + nums[k]; 14 if (sum1 > target && sum1 > 0) break; 15 //第二层去重 16 if (k > i+1 && nums[k] == nums[k-1]) continue; 17 //开始运用双指针 18 int left = k + 1, right = nums.length - 1; 19 while (left < right){ 20 long sum = nums[i] + nums[k] + nums[left] + nums[right]; 21 if (sum > target){ 22 right--; 23 }else if (sum < target){ 24 left++; 25 }else { 26 ans.add(Arrays.asList(nums[i], nums[k], nums[left], nums[right])); 27 //对left和right去重 28 while (left < right && nums[left] == nums[left + 1]) left++; 29 while (left < right && nums[right] == nums[right - 1]) right--; 30 left++; 31 right--; 32 } 33 } 34 } 35 } 36 return ans; 37 } 38 }
小知识:
List<String> list = Arrays.asList("a","b","c"):将数组转换成List集合
注意:
(1)该方法适用于对象型数据的数组(String、Integer...);
(2)该方法不建议使用于基本数据类型的数组(byte,short,int,long,float,double,boolean);
(3)该方法将数组与List列表链接起来:当更新其一个时,另一个自动更新;
(4)不支持add()、remove()、clear()等方法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2021-10-16 力扣495(java)-提莫攻击(简单)