15三数之和(经典题目)
https://leetcode.cn/problems/3sum/
给你一个整数数组
nums
,判断是否存在三元组[nums[i], nums[j], nums[k]]
满足i != j
、i != k
且j != k
,同时还满足nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为0
且不重复的三元组。**注意:**答案中不可以包含重复的三元组
题目分析: 即从一个给定数组找出三个数,满足三个数之和为0的要求,并返回这由三个数组成的数组,且数组的不可重复。
思路分析: 总的来讲可分为两大步骤:
1. 找出满足存储条件的数组
2. 去重存储
最优先想到的找数即三层嵌套for循环,这里先不做过多赘述,其次是去重存储联想到Hashset,还有需要注意的是在去重存储的过程中,使用Hashset时可能会出现单个数组中元素顺序不一致进而导致判断为不同数组问题,因此首先需要进行的是给存入数组排序,以确保存储满足条件的目标数组时元素出现的顺序是一致的。
1.Hash解法
有了上述思路,总的来讲Hash的话包含以下步骤:
- 嵌套for循环一前一后指针,后的那个指针每次从前指针的下一个指针开始
- 每一层开始时,新建一个Hash存储对象,进行条件判断,若满足条件进行排序并存入最终的Hashset中,若不满足则将后指针指向的数据存入该层的Haset存储对象中。
import java.awt.List;
import java.util.*;
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
HashSet<List<Integer>> resSet = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
HashMap<Integer, Integer> records = new HashMap<>();
for (int j = i+1; j < nums.length; j++) {
if (records.containsKey(0-(nums[i] + nums[j]))){
List<Integer> aList = Arrays.asList(nums[i], nums[j], 0 - (nums[i] + nums[j]));
//存入数组排序
Collections.sort(aList);
resSet.add(aList);
}else {
records.put(nums[j],nums[j]);
}
}
}
return new ArrayList<>(resSet);
}
}
//leetcode submit region end(Prohibit modification and deletion)
2. 双指针解法
双指针法两大指针之间的关系是什么?
双指针法指针的移动关键是什么(什么时候指针移动停下来)?
除了上述的Hash解法外,另外本题比较经典解法为双指针方法。无论是本题还是其他采用多指针法的题型,都离不开的是不同指针之间的关系,以及多个指针间的移动法则,以及什么时候使用双指针本题的双指针核心在于每一次外层循环遍历时,开始进行双指针初始化,即left指向外层指针旁边,right指针则指向末尾,而left和right之间的关系则是letf != right,总的来说为以下步骤:
- 给数组排序(数组预处理)
- 每一层定义好双指针left及right
- 进行条件判断若nums[i] + nums[left] +nums[right] == 0 则存入,Hashset ,nums[i] + nums[left] +nums[right] < 0 则偏小left指针右移,反之right左移,两者指针相等时则跳出内层指针移动。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
//List<List<Integer>> resList = new ArrayList<>();
HashSet<List<Integer>> resList = new HashSet<>();
int left,right;
for (int i = 0; i < nums.length - 2; i++) {
// List<Integer> records = new ArrayList<>();
left = i + 1;
right = nums.length - 1;
while (left != right){
if (nums[i] + nums[left] + nums[right] == 0){
resList.add(Arrays.asList(nums[i], nums[left], nums[right]));
left++;
}
else if (nums[i] + nums[left] + nums[right] > 0){
right--;
}else {
left++;
}
}
}
//去重
return new ArrayList<>(resList);
}
}
两种方法的时间复杂度均为O(n^2)