package com.maom.leetcode;
import com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @Title: LC_00015_ThreeSum
* @Author maohb
* @Date 2020/3/23 8:02 AM
* @Version 1.0
* @Description:
* 15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
*/
public class LC_00015_ThreeSum {
/**
* @description test main
* @param args
* @author maohb
*/
public static void main(String[] args) throws Exception {
// 测试
int[] nums = {-1, 0, 1, 2, -1, -4, -17, -17, -17, 10, 10, 10, 10, 7, 7};
System.out.println(JSON.toJSONString(threeSum(nums)));
List<String> lt = new ArrayList<>();
lt.add("0");
lt.add("18");
lt.add("2");
lt.add("7");
lt.add("4");
Collections.sort(lt); // list 排序
System.out.println(lt.toString());
}
/**
* 左右双指针 简化版 【经测试 同一代码提交测试结果有所不同 请忽略微小差异】
*/
public static List<List<Integer>> threeSum(int[] nums) {
if (nums == null || nums.length < 3) return Collections.emptyList();
Arrays.sort(nums);
if (nums[0] > 0 || nums[nums.length - 1] < 0) return Collections.emptyList();
List<List<Integer>> res = new ArrayList<List<Integer>>();
for (int i = 0; i < nums.length - 2; i++) {
if (nums[i] > 0) break;
if (nums[i] + nums[i + 1] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
int L = i + 1;
int R = nums.length - 1;
while (L < R) {
int sum = nums[i] + nums[L] + nums[R];
if (sum == 0) {
res.add(Arrays.asList(nums[i], nums[L], nums[R]));
while (L < R && nums[L] == nums[L + 1]) L++;
while (L < R && nums[R] == nums[R - 1]) R--;
L++;
R--;
}
else if (sum > 0) {
while (L < R && nums[R] == nums[R - 1]) R--;
R--;
}
else if (sum < 0) {
while (L < R && nums[L] == nums[L + 1]) L++;
L++;
}
}
}
return res;
}
/**
* 左右双指针 改进版 21ms 45M
*/
public static List<List<Integer>> threeSum_4(int[] nums) {
// 长度不够返回 =====> 1
if (nums.length < 3) return Collections.emptyList();
// 排序
Arrays.sort(nums);
// 最大数<0 或最小数>0 跳出 =====> 2
if (nums[nums.length - 1] < 0 || nums[0] > 0) return Collections.emptyList();
// 初始化返回结果集
List<List<Integer>> res = new ArrayList<List<Integer>>();
// 循环基准坐标i, 后面左右双指针, 所以长度 -2
for (int i = 0; i < nums.length - 2; i++) {
// 最小数>0 跳出 =====> 3
if (nums[i] > 0) break; // 注意:不是continue
// 前两个数和>0 跳出 =====> 4
if ((nums[i] + nums[i + 1]) > 0) break;
// 去掉重复结果
if (i > 0 && nums[i] == nums[i - 1]) continue;
// 初始化判断参数
int sum = -nums[i]; // 左右之和 + sum = 0
int left = i + 1; // 左下标
int right = nums.length - 1; // 右下标
// 左右下标循环判断
while (left < right) {
// 临时存储 =====> 5
int rightValue = nums[right];
// 右侧<0 跳出 ===>< 此情况已被 4优化
// if (rightValue < 0) break;
int leftValue = nums[left];
// 判断
if (leftValue + rightValue == sum) {
// 符合条件放入结果集 =====> 6
res.add(Arrays.asList(-sum, leftValue, rightValue));
// 值相同去重复
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
// 成功后 同时移动
left++; right--;
}
else if ((leftValue + rightValue) > sum) {
// while (left < right && nums[right] == nums[right - 1]) right--; 无加速效果(评估内外判断效率)
// 最大数right过大 左移
right--;
}
else {
// while (left < right && nums[left] == nums[left + 1]) left++;
// 默认右移left
left++;
}
}
}
return res;
}
/**
* 左右双指针 26ms 44.9M
*/
public static List<List<Integer>> threeSum_3(int[] nums) {
// 初始化返回结果集
List<List<Integer>> res = new ArrayList<List<Integer>>();
// 长度不够返回
if (nums.length < 3) return res;
// 排序
Arrays.sort(nums);
// 最大数<0 或最小数>0 跳出
if (nums[nums.length - 1] < 0 || nums[0] > 0) return res;
// 循环基准坐标i, 其他左右双指针j,k
for (int i = 0; i < nums.length; i++) {
// 去掉重复结果
if (i > 0 && nums[i] == nums[i - 1]) continue;
// 初始化判断参数
int sum = -nums[i]; // 左右之和 + sum = 0
int left = i + 1; // 左下标
int right = nums.length - 1; // 右下标
// 左右下标循环判断
while (left < right) {
if (nums[left] + nums[right] == sum) {
// 符合条件放入结果集
List<Integer> target = new ArrayList<Integer>();
target.add(nums[i]);
target.add(nums[left]);
target.add(nums[right]);
res.add(target);
// 先去重复
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
// 成功后 同时移动
left++; right--;
}
else if ((nums[left] + nums[right]) > sum) {
// 最大数right过大 左移
right--;
}
else {
// 默认右移left
left++;
}
}
}
return res;
}
/**
* 左右双指针 【原版引用】 33ms 45.5M
*/
public static List<List<Integer>> threeSum_2(int[] nums) {
List<List<Integer>> ans = new ArrayList();
int len = nums.length;
if(nums == null || len < 3) return ans;
Arrays.sort(nums); // 排序
for (int i = 0; i < len ; i++) {
if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
int L = i+1;
int R = len-1;
while(L < R){
int sum = nums[i] + nums[L] + nums[R];
if(sum == 0){
ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
while (L<R && nums[R] == nums[R-1]) R--; // 去重
while (L<R && nums[L] == nums[L+1]) L++; // 去重
L++;
R--;
}
else if (sum < 0) L++;
else if (sum > 0) R--;
}
}
return ans;
}
/**
* 第一次想出来的简单解题思路 超时 【暴力循环】
*/
public static List<List<Integer>> threeSum_1(int[] nums) {
// 返回结果集
List<List<Integer>> res = new ArrayList<List<Integer>>();
// 用于判断组合重复情况
List<String> distinctLt = new ArrayList<String>();
// 组合第一个数 i
for(int i = 0; i < nums.length; i++) {
// 组合第二个数 j
for(int j = 0; j < nums.length; j++){
// 不能重复下标
if(i == j){
continue;
}
// 组合第三个数 k
for(int k = 0; k < nums.length; k++){
// 不能重复下标
if(i == k || j == k){
continue;
}
// 判断组合条件 a + b + c = 0
if(nums[i] + nums[j] + nums[k] == 0){
// 值从小到大排序 toString 判断组合是否重复
List<Integer> ex = new ArrayList<Integer>();
ex.add(nums[i]);
ex.add(nums[j]);
ex.add(nums[k]);
Collections.sort(ex); // 排序
System.out.println(ex.toString());
if (distinctLt.contains(ex.toString())) {
// 重复
}
else {
// 添加到判断重复集合
distinctLt.add(ex.toString());
// 添加到结果集
res.add(ex);
}
}
}
}
}
return res;
}
}
LeetCode题解整理_15. 三数之和
最新推荐文章于 2022-07-22 17:01:49 发布