1. question: 三数之和(中等)
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
示例 1:
1 2
| 输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]]
|
示例 2:
示例 3:
提示:
- 0 <= nums.length <= 3000
- -105 <= nums[i] <= 105
2. answers
这道题肯定暴力破解肯定会超时的。那么能不能用两数之和的思路来做呢?比如遍历数组,逐个以当前元素作为基础元素,求解两数之和为该基础元素的反数组合。这样做的话,得需要去重,所以这样做仍然是超时了。
其实仔细想想,超时的原因就是存在许多无用的判断。如果可以根据某些现象来去除掉一些无用判断就好了。可以将数组进行排序(以升序为例),逐个以当前元素作为基础元素,以首尾双指针法对当前元素的后续元素进行遍历求解组合。如果当前组合大于0,那么肯定是尾指针向前移动,因为已经排序了,此时首指针向后移动就是无用的组合。小于0同理。之后再去重。
去重的话,每生成一个结果,就对当前已有所有结果进行比较,因为排序了,所以产生的结果也是有序的,只需要逐个比较元素即可。
代码如下所示:
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| public class Solution_0049_03 {
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
if(nums.length < 3) return result;
Arrays.sort(nums); for (int i : nums) { System.out.println(i); }
int base = 0; int head; int tail;
int tempResult;
while(base <= nums.length - 2 - 1) {
head = base + 1; tail = nums.length - 1;
while(head < tail) {
tempResult = nums[base] + nums[head] + nums[tail];
if(tempResult == 0) { List<Integer> temp = new ArrayList<>();
temp.add(nums[base]); temp.add(nums[head]); temp.add(nums[tail]);
if(!hasList(result, temp)) { result.add(temp); }
head += 1; }
if(tempResult < 0) { head += 1; }
if(tempResult > 0) { tail -= 1; }
}
base += 1; }
return result; }
public static boolean hasList(List<List<Integer>> result, List<Integer> temp) {
int sum;
for(List<Integer> item : result) {
sum = 0;
for (int i = 0; i < temp.size(); i++) {
if (item.get(i).equals(temp.get(i))) { sum += 1; } else { break; } }
if(sum == 3) { return true; } }
return false; }
public static void main(String[] args) { System.out.println();
int[] nums = {-1, 0, 1, 2, -1, -4};
List<List<Integer>> result = threeSum(nums);
System.out.println("结果如下所示:");
for (List<Integer> temp : result ) {
for (Integer integer : temp) { System.out.print(integer + "\t"); }
System.out.println(); } } }
|
怎么说呢,上述方法虽然在LeetCode上通过了,但是耗时非常大。其实排序很大程度上减少了无用比较,但是在去重方面仍然存在较大开销。那么什么时候会出现重复结果呢?因为已经排序了,所以相同元素肯定出现在相邻位置上,那么我们在移动指针的时候,只需要判断移动前后的两个元素是否相同,如果相同,则继续移动,这样就可以避免出现重复结果了。
注意,因为排序了,所以重复元素只有可能出现在相邻位置。可以看到,对数组排序的作用还是挺大的。代码如下所示:
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| public class Solution_0049_04 {
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
if(nums.length < 3) return result;
Arrays.sort(nums);
int base = 0; int head; int tail;
int tempResult;
while(base <= nums.length - 1 - 2) {
System.out.println(base);
if(nums[base] > 0) { return result; }
if(base != 0) { while(nums[base] == nums[base-1] && base <= nums.length - 1 - 2) base ++; }
head = base + 1; tail = nums.length - 1;
while(head < tail) {
tempResult = nums[base] + nums[head] + nums[tail];
if(tempResult == 0) { List<Integer> temp = new ArrayList<>();
temp.add(nums[base]); temp.add(nums[head]); temp.add(nums[tail]);
result.add(temp);
head++; tail--;
while(nums[head] == nums[head-1] && head < tail) head ++;
while(nums[tail] == nums[tail+1] && head < tail) tail --; }
if(tempResult < 0) { head ++; }
if(tempResult > 0) { tail --; } }
base += 1; }
return result; }
public static void main(String[] args) {
int[] nums = {-1, 0, 1, 2, -1, -4};
List<List<Integer>> result = threeSum(nums);
System.out.println("结果如下所示:");
for (List<Integer> temp : result ) {
for (Integer integer : temp) { System.out.print(integer + "\t"); }
System.out.println(); } } }
|
3. 备注
参考力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 (leetcode-cn.com),代码随想录 (programmercarl.com)。