import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class ThreeSum {
//测试
public static void main(String[] args) {
int[] input = {-1, 0, 1, 2, -1, -4};
ThreeSum threeSum = new ThreeSum();
System.out.println(threeSum.threeSum3(input));
}
/**
* 方法一:暴力破解 未去重
* @param nums
* @return
*/
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
//定义结果列表
List<List<Integer>> result = new ArrayList<>();
//三重for循环 枚举所有的三数组合
for (int i = 0; i < n-2; i++) {
for (int j = i+1; j <n-1 ; j++) {
for (int k = j+1; k < n; k++) {
if(nums[i]+nums[j]+nums[k]==0){
result.add(Arrays.asList(nums[i],nums[j],nums[k]));
}
}
}
}
return result;
}
/**
* 方法二:使用哈希表保存结果 未去重
* @param nums
* @return
*/
public List<List<Integer>> threeSum2(int[] nums){
int n = nums.length;
List<List<Integer>> result = new ArrayList<>();
// 定义一个hash map
HashMap<Integer, List<Integer>> map = new HashMap<>();
// 遍历数组,寻找每个数对应的那个数
for( int i = 0; i < n; i++ ){
int thatNum = 0 - nums[i];
if (map.containsKey(thatNum)){
// 如果已经存在thatNum,就找到了一组解
ArrayList<Integer> tempList = new ArrayList<>(map.get(thatNum));
tempList.add(nums[i]);
result.add(tempList);
}
// 把当前数对应的两数组合都保存到map里
for (int j = 0; j < i; j++ ){
// 以两数之和作为key
int newKey = nums[i] + nums[j];
// 如果key不存在,就直接添加进去
if (!map.containsKey(newKey)){
ArrayList<Integer> tempList = new ArrayList<>();
tempList.add(nums[i]);
tempList.add(nums[j]);
map.put( newKey, tempList );
}
}
}
return result;
}
/**
* 方法三:双指针法
* @param nums
* @return
*/
public List<List<Integer>> threeSum3(int[] nums) {
int n = nums.length;
//定义结果列表
List<List<Integer>> result = new ArrayList<>();
//1.先对数组进行排序
Arrays.sort(nums); //O(nlog(n))
//2.遍历每一个元素,作为当前三元组中最小的,定义左右指针 时间复杂度O(n^2)
for (int i = 0; i < n; i++) {
//2.1 如果当前最小元素大于0,直接退出循环
if(nums[i]>0)break;
//2.2 如果当前数出现过,pass
if(i>0&&nums[i]==nums[i-1]) continue;
//2.3 定义左右指针
int lp = i+1;
int rp = n-1;
//2.4 只要左指针保持小于右指针 继续移动指针
while (lp<rp){
int sum = nums[i]+nums[lp]+nums[rp];
//判断sum的值,和0进行比较
if(sum==0){
//2.4.1 sum==0 满足条件
result.add(Arrays.asList(nums[i],nums[lp],nums[rp]));
//左指针右移 右指针左移
lp++;
rp--;
//如果移动后的元素相同 直接跳过
while (lp<rp&&nums[lp]==nums[lp-1])lp++;
while (lp<rp&&nums[rp]==nums[rp+1])rp--;
}else if(sum<0){
//2.4.2 小了 故左指针右移
lp++;
}else {
//2.4.3 大了 故右指针左移
rp--;
}
}
}
return result;
}
}