Leetcode第18题,给定一个数组与一个target,找出数组中的四个数之和为target的不重复的全部四个数. java
List<List<Integer>> result = new ArrayList<>(); if (nums.length == 4 && nums[0] + nums[1] + nums[2] + nums[3] == target) result.add(Arrays.asList(nums[0], nums[1], nums[2],nums[3])); else if (nums.length > 4) { Arrays.sort(nums); Set<List<Integer>> resultSet = new HashSet<>(); for(int i=0;i<nums.length-3;++i) { for(int j=i+1;j<nums.length-2;++j) { for(int k=j+1;k<nums.length-1;++k) { for(int m=k+1;m<nums.length;++m) { if(nums[i]+nums[j]+nums[k]+nums[m] == target) resultSet.add(Arrays.asList(nums[i],nums[j],nums[k],nums[m])); } } } } result.addAll(resultSet); Collections.sort(result,(t1,t2)-> { if(t1.get(0) > t2.get(0)) return 1; if (t1.get(0) < t2.get(0)) return -1; if (t1.get(1) > t2.get(1)) return 1; if (t1.get(1) < t2.get(1)) return -1; if (t1.get(2) > t2.get(2)) return 1; if (t1.get(2) < t2.get(2)) return -1; if (t1.get(3) > t2.get(3)) return 1; if (t1.get(3) < t2.get(3)) return -1; return 0; }); } return result;
判断长度,而后排序,直接上四个for,而后... 好! 惨败.git
首先最后的排序是没必要要的,也就是后面的github
Collections.sort(result,(t1,t2)-> { if(t1.get(0) > t2.get(0)) return 1; if (t1.get(0) < t2.get(0)) return -1; if (t1.get(1) > t2.get(1)) return 1; if (t1.get(1) < t2.get(1)) return -1; if (t1.get(2) > t2.get(2)) return 1; if (t1.get(2) < t2.get(2)) return -1; if (t1.get(3) > t2.get(3)) return 1; if (t1.get(3) < t2.get(3)) return -1; return 0; });
对结果进行排序没必要要,虽然会在测试时与答案有差异,可是提交的话不须要排序.算法
以前的操做用的是HashSet进行去重,有一个符合的四元组就直接添加进集合中,如今采用了stream+distinct去重:数组
return result.stream().distinct().collect(Collectors.toList());
能够利用相似三数之和的思想,固定一个数,双指针分别指向两端的两个数,这里的话,四个数,选择固定两个数,计算它们的和并把它们看做一个数,便可利用双指针.测试
for(int i=0;i<nums.length-3;++i) { for(int j=i+1;j<nums.length-2;++j) { int m = nums[i] + nums[j]; int left = j+1; int right = nums.length-1; while(left < right) { int temp = m + nums[left] + nums[right]; if(temp == target) { result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); --right; ++left; } else if(temp > target) --right; else ++left; } } }
m为固定的数,left与right就是双指针,根据"三数"之和判断与目标值的大小移动双指针. 最小剪枝就是首先计算"三数"的最小值,若大于目标值就能够跳过,最大剪枝就是计算"三数"的最大值,若小于目标值则跳过,进入下一个循环:优化
int m = nums[i] + nums[j]; int left = j+1; int right = nums.length-1; if(m + nums[left] + nums[left+1] > target) continue; if (m + nums[right-1] + nums[right] < target) continue;
呃...好了那么一点点吧.指针
首先,初始的判断能够再简单一点,若是数组为空或长度小于4,直接返回.code
List<List<Integer>> result = new ArrayList<>(); if (nums == null && nums.length < 4) return result;
上面的算法中,只是在两层for里面进行了一次最大最小剪枝,能够在没进入for以前剪一次:blog
Arrays.sort(nums); int len = nums.length; if( nums[0] + nums[1] + nums[2] + nums[3] > target || nums[len-4] + nums[len-3] + nums[len-2] + nums[len-1] < target ) return result; for(int i=0;i<len-3;++i)
注意要先排序,而后直接判断整个数组的最大最小值并与target判断. 而后在进入第一层for以后再剪一次:
for(int i=0;i<len-3;++i) { if(nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) break; if(nums[i] + nums[len-3] + nums[len-2] + nums[len-1] < target) continue; for(int j=i+1;j<len-2;++j) }
由于数组是升序排序的,所以,"最左边"四个数确定是最小值,若这个最小值大于target,能够直接break了,可是,最右边三个数与nums[i]相加不必定为最大值,所以判断以后若小于target只能continue.
首先,在双指针的循环中,若发现了有四个数符合条件,能够尝试屡次移动指针:
result.add(Arrays.asList(nums[i], nums[j], nums[left++], nums[right--])); while(left < right && nums[left] == nums[left-1]) ++left; while(left < right && nums[right] == nums[right+1]) --right;
由于值同样的能够一次性移动指针,不须要再次进行和的判断. 呃,能够尝试提交了.
咦,不对啊,作了这么多,没快多少啊... 为啥呢... ...
找了好久,发现是这里的缘由:
return result.stream().distinct().collect(Collectors.toList());
这里去重的话,用是用的很舒服,一个stream(),一个distinct()就行了,问题是...仍是很慢啊!!! 因此呢,须要手动去重,出现重复的缘由就是数组中有重复的数,好比:
[1,1,1,1,2,2,2,2],target=6
顺序判断时,会好几个
[1,1,2,2]
所以,对于重复的数,进行跳过处理,在第一层for中,对重复过的进行跳过:
for(int i=0;i<len-2;++i) if(i>0 && nums[i] == nums[i-1]) continue;
其次,在第二层for中,也对重复过的进行跳过:
for(int j=i+1;j<len-2;++j) if(j > i+1 && nums[j] == nums[j-1]) continue;
这样的话,例如对于上面的(不一样的1用字母区分)
[1(a),1(b),1(c),1(d),2,2,2,2]
一开始是a处的1与b处的1,而后到了第二层循环,由于此时j=i+1,指向b处的1,所以不会跳过1,会进入双指针循环,第二次j指向c处的1,出现重复,j不断跳过直到j指向2.而后2结束后,到了i这层循环,由于1出现过,i不断跳过直到i指向2.
没错,说了这么多,去重不须要什么HashSet,不须要什么stream,只需两行:
if(i>0 && nums[i] == nums[i-1]) continue; if(j>i + 1 && nums[j] == nums[j-1]) continue;
尽力了.