![]()
1 package threesum;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.List;
6
7 /**
8 * 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
9 * 注意:答案中不可以包含重复的三元组
10 *
11 * 主要的思想:如果采用暴力法的话,时间复杂度很高出现超时的情况。
12 * 对暴力法进行一系列的改进 先从数组的开始取出元素,在剩下的数中设置首尾指针, 通过首尾指针的移动来遍历其中所有的数字。
13 * 具体的细节问题 会通过代码注释的方式进行记录
14 * @author Saint
15 *
16 */
17
18 public class Solution {
19 public static void main(String[] args) {
20 int[] nums = {-1,0,1,2,-1,-4};
21 ArrayList<List<Integer>> answers = new ArrayList<List<Integer>>();
22 answers = (ArrayList<List<Integer>>) threeSum(nums);
23 for (List<Integer> list : answers) {
24 System.out.println(list);
25 }
26 }
27
28 public static List<List<Integer>> threeSum(int[] nums) {
29 /*
30 * 不能使用ArrayList对其中重复的数值进行去重
31 */
32 /*
33 * ArrayList<Integer> list = new ArrayList<Integer>(); for (int i = 0; i
34 * < nums.length; i++) { list.add(nums[i]); }
35 */
36 ArrayList<List<Integer>> answers = new ArrayList<List<Integer>>();
37 /*
38 * 对数组进行排序,后面会进行说明
39 */
40 Arrays.sort(nums);
41 for (int i = 0; i < nums.length; i++) {
42 System.out.println(i);
43 /*
44 * 设置首尾指针 left与right不能重合
45 */
46 for (int left = i + 1, right = nums.length - 1, num = nums[i]; left < right;) {
47 System.out.println(num + " " + nums[left] + " " + nums[right]);
48
49 if (num + nums[left] + nums[right] == 0) {
50 System.out.println("equal");
51 ArrayList<Integer> temp = new ArrayList<Integer>();
52 temp.add(num);
53 temp.add(nums[left]);
54 temp.add(nums[right]);
55 answers.add(temp);
56 /*
57 * 如果相同,添加到集合中,同时首尾指针需要跳过当前的位置
58 */
59 left = moveLeft(nums, left, right);
60 right = moveRight(nums, left, right);
61 /*
62 * 通过之前的数组排序,数组从大到小排列 例如:5 4 3 2 1
63 * 当首尾指针两数之和大于nums[i]时, 需要减小数字 left++或者right-- 对应数组就会变小(显然不可能)
64 * 这一点是我觉得比较精妙的一点 首尾移动需要遍历出所有的情况 不能像我之前想法 左移动一个 右移动一个
65 * 通过暴力的方法更不推荐
66 */
67 } else if (nums[left] + nums[right] > 0) {
68 left = moveLeft(nums, left, right);
69 } else {
70 right = moveRight(nums, left, right);
71 }
72 }
73 }
74 return answers;
75 }
76 /**
77 * 取出重复的数字 例如:1 1 -2 -2 -2这个数组
78 * 当三数之和相等时,left=1 right=4 这时需要left++ right-- 跳过当前位置同时检查下个位置是否和前一个位置是否为相同
79 * right=3和之前的数字相同 继续right--知道nums[right]不和之前数字相同
80 * 之后执行到left = right 循环结束返回结果 for循环不成立 结束整个过程
81 * @param nums
82 * @param left
83 * @param right
84 * @return
85 */
86 public static int moveLeft(int[] nums, int left, int right) {
87 int num = nums[left++];
88 while (left <= right) {
89 if (nums[left] != num)
90 break;
91 left++;
92 }
93 return left;
94
95 }
96
97 public static int moveRight(int[] nums, int left, int right) {
98 int num = nums[right--];
99 while (left <= right) {
100 if (nums[right] != num)
101 break;
102 right--;
103 }
104 return right;
105
106 }
107 }