三数之和(剑指 Offer II 007. 数组中和为 0 的三个数 && 15. 三数之和)
题目:
思路:
【1】基于正常思维便是暴力破解,但是这种时间复杂度为O(N^3),基本很容易超时。而且还要去重【这里由于是无序的,所以又要用到排序】。
【2】那么既然是要排序的何不一开始就将无序的数组变为有序呢,一旦变为了有序,那么问题处理起来就会变得简单起来:
如数组[-4,-1, -1, 0, 1, 2]
先遍历数组,如果i=0,先取-4,那么
第一个指针a指向下标为1的-1
第二个指针b指向下标为4的2
首先面对双指针,若a+b+i < 0,那么说明数需要加大
则指向最小的a,需要右移加大
若a+b+i > 0,那么说明数需要减少
则指向最大的b,需要左移减小
若a+b+i == 0,则需要纳入结果元组中
而这些过程中 a<b ,因为这样才会保证数据还没遍历完,还会有其他的
如果出现了a>=b,则说明数据重复使用或者重复遍历的过程,都是不需要的
代码展示:
暴力破解的做法:
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<>(); for (int k0 = 0; k0 < nums.length - 2;k0++){ for (int k1 = k0+1; k1 < nums.length - 1;k1++){ for (int k2 = k1+1; k2 < nums.length;k2++){ if (nums[k0]+nums[k1]+nums[k2] == 0){ ArrayList<Integer> objects = new ArrayList<>(Arrays.asList(nums[k0],nums[k1],nums[k2])); result.add(objects); } } } } //排序 for (int i = 0;i<result.size();i++){ Collections.sort(result.get(i)); } //去重 HashSet<List<Integer>> set = new HashSet<>(result); result.clear(); result.addAll(set); return result; } }
基于先排序然后采用双指针的方式进行处理:
//执行用时:43 ms, 在所有 Java 提交中击败了12.68%的用户 //内存消耗:48 MB, 在所有 Java 提交中击败了5.01%的用户 class Solution { public List<List<Integer>> threeSum(int[] nums) { Arrays.sort(nums); List<List<Integer>> result = new ArrayList<>(); int a,b,sum; for (int i = 0; i < nums.length - 2;i++){ //如果最小值都是大于0的情况下,那么就没有必要继续遍历下去 if (nums[i] > 0){ break; } //在这里进行去重,如[-1, -1, 0, 1, 2],当第一个-1已经处理过了,那么第二个就没有必要了,元组已经被包含了 if (i>0&&(nums[i] == nums[i-1])){ continue; } a = i+1; b = nums.length-1; //采用双指针的方式,这样就能将暴力破解里面时间复杂度为O(N^2)部分转化为O(N) while (a<b){ sum = nums[i] + nums[a] + nums[b]; if (sum>0){ //这里采用了循环的特性进行去重处理 while (a < b && nums[b] == nums[--b]) ; }else if (sum<0){ while (a < b && nums[a] == nums[++a]) ; }else { result.add(new ArrayList<>(Arrays.asList(nums[i], nums[a], nums[b]))); while (a < b && nums[b] == nums[--b]) ; while (a < b && nums[a] == nums[++a]) ; } } } return result; } }