1. 题目描述
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组。 示例 1:
输入: nums = [-1,0,1,2,-1,-4]
输出: [[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入: nums = [0,1,1]
输出: []
解释: 唯一可能的三元组和不为 0 。
示例 3:
输入: nums = [0,0,0]
输出: [[0,0,0]]
解释: 唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000-105 <= nums[i] <= 105
2.收获
这道题在代码随想录中是放在哈希中的,所以在第一手看见题目时有些不确定最优解是什么,也完全没想到双指针法。这也是第一道卡了我好几天的题目,有点不太想写。。但是困难还是要攻克的,而且认真看了一遍题解后发现也没有那么难哈,虽然自己想是想不到的哈哈。
首先总结下学到的java知识点吧;
首先是util下的Arrays
java.util.Arrays
https://www.runoob.com/manual/jdk11api/java.base/java/util/Arrays.html
该类包含用于操作数组的各种方法(例如排序和搜索)。 此类还包含一个静态工厂,允许将数组视为列表。
如果指定的数组引用为null,则此类中的方法都抛出`NullPointerException` ,除非另有说明。
这道题用到的方法有:
1.排序
Arrays.sort(nums);
2.Arrays.asList();
static void | sort(int[] a) | 将指定的数组按升序排序。 |
|---|---|---|
static void | sort(int[] a, int fromIndex, int toIndex) | 按升序对数组的指定范围进行排序。 |
static <T> List<T> | asList(T... a) | 返回由指定数组支持的固定大小的列表。 |
|---|
这里的难点主要有:
- 思路,先排序然后用双指针,
- 去重
自己写的代码:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
for(int k = 0; k < nums.length; k++) {
if(nums[k] > 0) break;
int i = k + 1;
int j = nums.length - 1;
if(k > 0 && nums[k] == nums[k-1]) continue;
while(i < j) {
if(nums[k] + nums[i] + nums[j] == 0) {
List<Integer> l = new ArrayList<>();
l.add(nums[k]);
l.add(nums[i]);
l.add(nums[j]);
list.add(l);
while(i < j && nums[i] == nums[++i]);
while(i < j && nums[j] == nums[--j]);
}
if(nums[k] + nums[i] + nums[j] < 0) {
do {
i++;
}while(i<j && nums[i] == nums[i-1]);
continue;
}
if(nums[k] + nums[i] + nums[j] > 0) {
do {
j--;
}while(i<j && nums[j] == nums[j+1]);
continue;
}
}
}
return list;
}
}
题解:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);//排序,nums变成递增数组
List<List<Integer>> res = new ArrayList<>();
//k < nums.length - 2是为了保证后面还能存在两个数字
for(int k = 0; k < nums.length - 2; k++){
if(nums[k] > 0) break;//若nums[k]大于0,则后面的数字也是大于零(排序后是递增的)
if(k > 0 && nums[k] == nums[k - 1]) continue;//nums[k]值重复了,去重
int i = k + 1, j = nums.length - 1;//定义左右指针
while(i < j){
int sum = nums[k] + nums[i] + nums[j];
if(sum < 0){
while(i < j && nums[i] == nums[++i]);//左指针前进并去重
} else if (sum > 0) {
while(i < j && nums[j] == nums[--j]);//右指针后退并去重
} else {
res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
while(i < j && nums[i] == nums[++i]);//左指针前进并去重
while(i < j && nums[j] == nums[--j]);//右指针后退并去重
}
}
}
return res;
}
}
复杂度分析:
-
时间复杂度:其中固定指针k循环复杂度 ,双指针 i,j 复杂度 。
-
空间复杂度 :指针使用常数大小的额外空间。