LeetCode第[18]题(Java):4Sum (四个数的和为目标值)——Medium
题目难度:Medium
题目:
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note: The solution set must not contain duplicate quadruplets.
翻译:
给定一个n个整数的数组S,在S中是否存在有a、b、c、d的元素使得a+b+c+d=target?如果有,请找出数组中所有唯一的组合。
注意:解决方案集不能包含重复的四胞胎。
【其实和3Sum是一个意思】
思路:既然是在3Sum的基础上加了一个数,那么请允许我可耻的想到了在外面再加。。。。
。。。。。。可是我实在想不到别的了,,外面的循环没有一个标准,所以也不可能在外面再用一遍left与right双重指针。
嗯,那就可耻地写吧
【注意:很多同学面对算法题其实并不是没有思路,而是多多少少有些思路,但是心里知道这个思路很low写了也没多大意思,干脆就不写了而迫不及待去看答案。
这就大错特错了,其实大多数的算法都是由简单算法演变而来的,只是在low算法的基础上或多或少地将冗余的步骤技巧性跳过了。
都不把自己的思路写出来,那么当面对一个从没见过的算法题时思路几乎永远是空白。
况且,平时写简单的计算时各种边界问题的处理也能使我们养成良好的编程习惯和对代码的掌控力。
我们不能为了刷题而刷题,应该是为了锻炼自己的思路。以上个人见解,欢迎讨论】
Code:282 / 282 test cases passed.——72ms(beats 46.27%) 时间复杂度:O(N3)
1 public List<List<Integer>> fourSum(int[] nums, int target) { 2 Arrays.sort(nums); 3 List<List<Integer>> result = new ArrayList<List<Integer>>(); 4 5 for (int i = 0; i < nums.length - 3; i++) { 6 if (i > 0 && nums[i] == nums[i-1]) continue; 7 for (int j = i + 1; j < nums.length -2; j++) { 8 if (j > i+1 && nums[j] == nums[j-1]) continue; // 注意j是从i+1开始 9 int left = j + 1; 10 int right = nums.length -1; 11 while (left < right) { 12 int sum = nums[i] + nums[j] + nums[left] + nums[right]; // 和3sum一个思路 13 14 if (sum == target) { 15 result.add(Arrays.asList(new Integer[]{nums[i], nums[j], nums[left], nums[right]})); 16 while (left < right && nums[right-1] == nums[right]) right--; 17 while (left < right && nums[left+1] == nums[left]) left++; 18 right--; 19 left++; 20 } else if (sum > target) { 21 right--; 22 } else { 23 left++; 24 } 25 } 26 } 27 } 28 return result; 29 }
以上直接套用3sum那道题的“双指针法”,然后在外面加了一个for循环,没啥好说的。 3sum:LeetCode第[15]题(Java):3Sum 标签:Array
过程错误:
1.命名错误:length 写成 leng
2.忘记定义类名:直接写成了left = j+1; (left前少写了int)【这就是不建议用(My)Eclipse做算法题的原因,脱离IDE可以改掉很多不好的编码习惯,也对将来面试手写代码帮助不少】
3.忘记写
1 else if (nums[left] + nums[right] < sum) { 2 left++; 3 } else { 4 right--; 5 }
这一部分了,导致最后输出为空,在写if的时候应该把所有的else if 和else都写完,以防止遗漏。这也说明自己还是在凭借对之前用过的算法的记忆在写代码,而没有真正的做到理解后的运用自如,
所以即使自信对某一个算法很熟悉,在写的时候也应该思考它的思想和逻辑,以防止出现关键性的遗漏。
答案1——Cdoe:282 / 282 test cases passed.——29ms(beats 81.40%) 时间复杂度:O(N3)
1 public List<List<Integer>> fourSum(int[] nums, int target) { 2 List<List<Integer>> res=new ArrayList<>(); 3 if(nums.length<4) return res; 4 Arrays.sort(nums); 5 for(int i=0;i<nums.length-3;i++){ 6 if(i>0&&nums[i]==nums[i-1]) continue; 7 8 if(nums[i]*4>target) break;// Too Big!!太大了,后续只能更大,可以直接结束循环; 9 if(nums[i]+3*nums[nums.length-1]<target) continue;//Too Small!太小了,当前值不需要再算,可以继续循环尝试后面的值。 10 11 for(int j=i+1;j<nums.length-2;j++){ 12 if(j>i+1&&nums[j]==nums[j-1]) continue; 13 14 if(nums[j]*3>target-nums[i]) break;//Too Big! 注意此时不能结束i的循环,因为j是移动的 当j移动到后面的时候继续i循环也sum可能变小 15 if(nums[j]+2*nums[nums.length-1]<target-nums[i]) continue;// Too Small 16 17 int begin=j+1; 18 int end=nums.length-1; 19 while(begin<end){ 20 int sum=nums[i]+nums[j]+nums[begin]+nums[end]; 21 if(sum==target){ 22 res.add(Arrays.asList(nums[i],nums[j],nums[begin],nums[end])); 23 while(begin<end && nums[begin]==nums[begin+1]){begin++;} 24 while(begin<end && nums[end]==nums[end-1]){end--;} 25 begin++; 26 end--; 27 }else if (sum<target){ 28 begin++; 29 }else{ 30 end--; 31 } 32 } 33 } 34 } 35 return res; 36 }
和我的解法是一样的,但是多加了一些条件判断,去除了一些重复数据和过大过小数据:
当前起点 i,肯定是此后数组中能组合的四个数中最小的,4*nums[i] 都大于target的话那么说明此后数组中任何组合都会比target大,此时可以直接结束当前循环。【j循环同理】
如果nums[i] + 3nums[len-1]肯定是不会比后续任何组合小的,如果这个都小于target,那么说明以nums[i]开头的后续任何组合都会比target小,此时可以进入下一层循环。【j循环同理】
这样在很多测试用例时能省下不少时间。
面试手写的时候大家可以秀一秀这样的操作,但是做公司的笔试题的时候因为做题时间有限,除非已经都AC,否则优先保证完整性即可。
这波强行解释怎么样!
答案2——Code:282 / 282 test cases passed.——28ms(beats 83.45%) 时间复杂度:O(N3)
1 public List<List<Integer>> fourSum(int[] nums, int target) { 2 ArrayList<List<Integer>> res = new ArrayList<List<Integer>>(); 3 int len = nums.length; 4 if (nums == null || len < 4) 5 return res; 6 7 Arrays.sort(nums); 8 9 int max = nums[len - 1]; 10 if (4 * nums[0] > target || 4 * max < target) 11 return res; 12 13 int i, z; 14 for (i = 0; i < len; i++) { 15 z = nums[i]; 16 if (i > 0 && z == nums[i - 1])// avoid duplicate 17 continue; 18 if (z + 3 * max < target) // z is too small 19 continue; 20 if (4 * z > target) // z is too large 21 break; 22 if (4 * z == target) { // z is the boundary 23 if (i + 3 < len && nums[i + 3] == z) 24 res.add(Arrays.asList(z, z, z, z)); 25 break; 26 } 27 28 threeSumForFourSum(nums, target - z, i + 1, len - 1, res, z); 29 } 30 31 return res; 32 } 33 34 /* 35 * Find all possible distinguished three numbers adding up to the target 36 * in sorted array nums[] between indices low and high. If there are, 37 * add all of them into the ArrayList fourSumList, using 38 * fourSumList.add(Arrays.asList(z1, the three numbers)) 39 */ 40 public void threeSumForFourSum(int[] nums, int target, int low, int high, ArrayList<List<Integer>> fourSumList, 41 int z1) { 42 if (low + 1 >= high) 43 return; 44 45 int max = nums[high]; 46 if (3 * nums[low] > target || 3 * max < target) 47 return; 48 49 int i, z; 50 for (i = low; i < high - 1; i++) { 51 z = nums[i]; 52 if (i > low && z == nums[i - 1]) // avoid duplicate 53 continue; 54 if (z + 2 * max < target) // z is too small 55 continue; 56 57 if (3 * z > target) // z is too large 58 break; 59 60 if (3 * z == target) { // z is the boundary 61 if (i + 1 < high && nums[i + 2] == z) 62 fourSumList.add(Arrays.asList(z1, z, z, z)); 63 break; 64 } 65 66 twoSumForFourSum(nums, target - z, i + 1, high, fourSumList, z1, z); 67 } 68 69 } 70 71 /* 72 * Find all possible distinguished two numbers adding up to the target 73 * in sorted array nums[] between indices low and high. If there are, 74 * add all of them into the ArrayList fourSumList, using 75 * fourSumList.add(Arrays.asList(z1, z2, the two numbers)) 76 */ 77 public void twoSumForFourSum(int[] nums, int target, int low, int high, ArrayList<List<Integer>> fourSumList, 78 int z1, int z2) { 79 80 if (low >= high) 81 return; 82 83 if (2 * nums[low] > target || 2 * nums[high] < target) 84 return; 85 86 int i = low, j = high, sum, x; 87 while (i < j) { 88 sum = nums[i] + nums[j]; 89 if (sum == target) { 90 fourSumList.add(Arrays.asList(z1, z2, nums[i], nums[j])); 91 92 x = nums[i]; 93 while (++i < j && x == nums[i]) // avoid duplicate 94 ; 95 x = nums[j]; 96 while (i < --j && x == nums[j]) // avoid duplicate 97 ; 98 } 99 if (sum < target) 100 i++; 101 if (sum > target) 102 j--; 103 } 104 return; 105 }
呃,还写了两个调用函数。。,
它在主函数里面循环调用了3sum的算法,然后加了一些判断,其实就是和答案1一样的也就是和我的也是一样的。。。。。。