题目
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
解题步骤:
核心逻辑:
初次看到此题时,三数之和为0,分为三种情况:两负一正,两正一负,还有三个0。
因此我们可以设置目标数组的每一个元素为每一轮循环的标记位置now,然后它后面的元素为左边指针left的起始点,目标数组的最后一个元素为右边指针的起始点right,然后通过判断三者对应的值,来控制指针的移动。
如果三数之和<0,移动左指针。
如果三数之和=0,则进行判重,将左右指针分别移动到与当前指针不相同的位置。
如果三数之和>0,则移动右指针。
特殊值判断:
- 三数之和,则长度小于3项则直接淘汰。
- 标记位置now对应的元素是三个数中最小的一个元素,要么是负数,要么是0,所以标记元素大于0则直接淘汰。
- 如果当前标记位置now对应的元素与now-1位置对应的元素相等,则直接跳过当前轮次,去重。
- 左指针
- 因为左指针指向的位置比now的位置大1,因此要进行边界值的判断。即now最大取length-1。 初始:left=now+1;
- 左指针要始终比右指针小 left<right
代码展示
1.排序+双指针(最优解)
public static List<List<Integer>> threeSum(int[] nums) {
//排序
Arrays.sort(nums);
List<List<Integer>> resList = new ArrayList<>();
for (int i = 0; i < nums.length - 1; i++) {
//标记元素必须大于0
if (nums[0] > 0) return resList;
//当前标记元素和之前的标记元素相等,跳过本轮循环,进行去重操作
if (i > 0 && nums[i] == nums[i - 1]) continue;
//标记元素,左指针,右指针
int f = nums[i], left = i + 1, right = nums.length - 1;
//左指针必须小于右指针
while (left < right) {
int s = nums[left];
int t = nums[right];
int total = f + s + t;
if (total < 0) {
left++;
} else if (total == 0) {
List<Integer> list = new ArrayList<>();
list.add(f);
list.add(s);
list.add(t);
resList.add(list);
//获取右边最后一个与当前元素相等的数
while (left < right && nums[right - 1] == nums[right]) right--;
//获取左边最后一个与当前元素相等的数
while (left < right && nums[left + 1] == nums[left]) left++;
//--和++ 目的是获取左右两边与当前s和t都不相等的数
right--;
left++;
} else {
right--;
}
}
}
return resList;
}
2.排序+双指针(初版,未优化,可通过)
问题:
- 目标数组的轮询使用了while循环。
- 将判重交给了Set集合判断,重复元素并没有在逻辑中去除。
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> resList = new ArrayList<>();
if (nums.length < 3) {
return resList;
}
Arrays.sort(nums);
int now = 0;
while (nums[now] <= 0 && now <= nums.length - 3) {
if (now > 0 && nums[now] == nums[now - 1]) {
now++;
continue;
}
int left = now + 1;
int right = nums.length - 1;
while (left < right && left < nums.length - 1 && right > 0) {
int first = nums[now];
int second = nums[left];
int third = nums[right];
int total = first + second + third;
if (total < 0) {
left++;
continue;
}
if (total == 0) {
List<Integer> list = new ArrayList<>();
list.add(first);
list.add(second);
list.add(third);
resList.add(list);
left++;
continue;
}
right--;
}
now++;
}
Set<List<Integer>> sets = new HashSet<>();
resList.forEach(item -> sets.add(item));
List<List<Integer>> lastRes = sets.stream().collect(Collectors.toList());
return lastRes;
}
3. 将正数和负数分开(暴力破解,超时)
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if (nums == null || nums.length < 3) {
return handleRes(result);
}
List<Integer> negatives = new ArrayList<Integer>();
List<Integer> positive = new ArrayList<Integer>();
int i = 0;
for (int num : nums) {
//0作为正数处理
boolean a = num < 0 ? negatives.add(num) : positive.add(num);
if (num == 0) {
i++;
}
}
if (i >= 3) {
List<Integer> list = new ArrayList<>();
for (int j = 0; j < 3; j++) {
list.add(0);
}
result.add(list);
}
//无整数,直接返回
if (positive.size() == 0) {
return handleRes(result);
}
//无负数,判断0的个数
if (negatives.size() == 0) {
return handleRes(result);
}
//两个正数,一个负数
getRes(positive, negatives, result, true);
//两个负数,一个正数
getRes(negatives, positive, result, false);
return handleRes(result);
}
/**
* @description: 方法一的结果返回值处理
* @param: null
* @return:
* @author zhq
* @date: 2021/10/20 20:04
*/
public static List<List<Integer>> handleRes(List<List<Integer>> res) {
Set<List<Integer>> list = new HashSet<>();
res.forEach(item -> list.add(item));
List<List<Integer>> resList = new ArrayList<>();
list.forEach(item -> {
resList.add(item);
});
return resList;
}
/**
* @description: 方法一的正数或负数处理
* @param: null
* @return:
* @author zhq
* @date: 2021/10/20 20:05
*/
public static void getRes(List<Integer> evenList, List<Integer> singularList, List<List<Integer>> result, boolean isPositive) {
for (int i = 0; i < evenList.size() - 1; i++) {
int first, second;
for (int j = i + 1; j < evenList.size(); j++) {
first = evenList.get(i);
second = evenList.get(j);
if (first == second && first == 0) {
//如果包含0,说明两个数都是整数,不符合条件
continue;
}
for (int m = 0; m < singularList.size(); m++) {
int third = singularList.get(m);
if (first + second + third == 0) {
List<Integer> res = new ArrayList<Integer>();
if (isPositive) {
res.add(third);
if (first < second) {
res.add(first);
res.add(second);
} else {
res.add(second);
res.add(first);
}
} else {
if (first < second) {
res.add(first);
res.add(second);
} else {
res.add(second);
res.add(first);
}
res.add(third);
}
result.add(res);
}
}
}
}
}
}