问题描述
给出一个数组,要求找出其中三个数相加值为 0 的所有组合。需要注意的是,输出不能包括相同的三元数组。例如:[1, 0, -1]、[1, -1, 0]、[-1, 0, 1]、[-1, 1, 0]、[1, -1, 1]、[0, 1, -1]这几个属于相同的三元数组,只需要输出其中个任何一个即可。题目链接:**点我**
样例输入输出
输入:[-1, 0, 1, 2, -1, -4]
输出:[[-1, 0, 1], [-1, -1, 2]]
问题解法
此问题不能使用暴力的做法,这样至少需要三层循环,时间复杂度为O(n^3)
,再加上对三元数组的去重,可能需要增加额外的循环导致增加额外的复杂度。即使将三元数组用一个类来表示,用set对其进行去重,整个求解过程也需要花费O(n^3)
的时间复杂度,很容易超时。因此需要寻求另外的解法方法。
使用暴力主要是因为数组是无序的,如果数组是有序的,可以使用双头指针来减少一层循环。主要做法如下
- 对数组进行升序排序得到有序的数组
- 对数组进行遍历,此次遍历是获得第三元数组中的第一个数,在此层遍历中,需要跳过已经遍历过的相同的项,这样既不会产生相同的三元数组,更能节省不必要的循环
- 在内部循环中,定义首尾两个指针,依次判断首指针数、尾指针数、外层循环数三者之和是否为0。
- 如果为0,则加入结果链表中。同时移动首尾指针跳过相同的项
- 如果大于0,说明尾指针的数过大,要将尾指针向前移动一位,然后继续循环比较
- 如果小于0,说明首指针的数过小,要将首指针向后移动一位,然后继续循环比较
此过程,排序时间复杂度为O(nlogn)
,获取所有结果是两层循环,时间负责度为O(n^2)
,总体时间复杂度为O(n^2)
。代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| class Solution { public List<List<Integer>> threeSum(int[] nums) { if (nums == null || nums.length < 3) { return new ArrayList<>(); } Arrays.sort(nums); int len = nums.length; List<List<Integer>> result = new ArrayList<>(); for (int i = 0; i < len; i++) { if (i > 0 && nums[i] == nums[i - 1]) { continue; } int start = i + 1; int end = len - 1; while (start < end) { if (nums[i] + nums[start] + nums[end] == 0) { List<Integer> triplet = new ArrayList<>(3); triplet.addAll(Arrays.asList(nums[i], nums[start], nums[end])); result.add(triplet); while (start < len - 1 && nums[start] == nums[start + 1]) { start++; } while (end > 0 && nums[end] == nums[end - 1]) { end--; } start++; end--; } else if (nums[i] + nums[start] + nums[end] > 0) { end--; } else { start++; } } } return result; } }
|
参考来源
- https://leetcode.com/problems/3sum/discuss/7380/Concise-O(N2)-Java-solution
- https://leetcode.com/problems/3sum/discuss/134544/my-solution-in-java-O(n2)