15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
思路:双指针法
思路参考:https://leetcode-cn.com/problems/3sum/solution/hua-jie-suan-fa-15-san-shu-zhi-he-by-guanpengchn/
总体思路:对于每个数nums[i], 判断它后面是否存在两个数,使得这两个数加上 nums[i] 的和刚好为0
具体实现过程:
先对nums[]数组进行排序
遍历nums[]数组,对于每个数nums[i], 判断它后面是否存在两个数,使得这两个数加上 nums[i] 之后的和刚好为0
如果nums[i]大于0, 因为nums[ ]数组已经按从小到大排序,所以可以断定nums[i]后面不存在两个数能使的三数之和等于0,且nums[i]后面的元素也不符合条件,不需要继续枚举下去,直接 break 跳出循环。
如果nums[i] == nums[i-1],无论nums[i]是否存在合法解,它的所有合法情况都已经在nums[i-1]被枚举过了,所以应该跳过。
开始寻找符合条件的解:让左指针等于i+1, 右指针等于len - 1
- 如果三数之和大于0, 右指针应该左移使得和能够减小一些
- 如果三数之和小于0, 左指针应该右移使得总和能够增大一些
- 如果三数之和等于0, 说明找到了一个合法的解,记录下来,此时,如果nums[left] = nums[left+1], left应该后移一位,因为nums[i]已经固定了,如果nums[left+1]也和上一个解一样的话,则剩下的一个数肯定也是一样的,则此次统计的一定是一个重复解,同理右指针
1 class Solution { 2 public List<List<Integer>> threeSum(int[] nums) { 3 4 List<List<Integer>> res = new ArrayList<>(); 5 if(nums == null || nums.length == 0){ 6 return res; 7 } 8 9 Arrays.sort(nums); // 排序 10 11 int len = nums.length; 12 for(int i = 0; i < len; i++){ 13 if(nums[i] > 0){ 14 break; 15 } 16 if(i > 0 && nums[i] == nums[i-1]){ // 去重 17 continue; 18 } 19 int left = i + 1, right = len - 1; 20 while(left < right){ 21 int sum = nums[i] + nums[left] + nums[right]; 22 if(sum == 0){ //如果三数之和等于0, 说明找到了一个合法的解,记录下来, 23 res.add(Arrays.asList(nums[i], nums[left], nums[right])); 24 while(left < right && nums[left] == nums[left + 1]){ // 跳过重复元素 25 left++; 26 } 27 while(left < right && nums[right] == nums[right-1]){ // 跳过重复元素 28 right--; 29 } 30 left++; 31 right--; 32 }else if(sum > 0){ //如果三数之和大于0, 右指针应该左移使得和能够减小一些 33 right--; 34 }else{ //如果三数之和小于0, 左指针应该右移使得总和能够增大一些 35 left++; 36 } 37 } 38 } 39 return res; 40 } 41 }
leetcode 执行用时:23 ms > 84.00%, 内存消耗:42 MB > 97.62%
复杂度分析:
时间复杂度:O(n2)。排序的时间复杂度为O(nlogn)。接下来循环遍历每个nums[i], 使用双指针法遍历这个nums[i]后面的所有元素是否存在符合条件的解,双指针的复杂度为O(n), 所以总的时间复杂度为O(n2)。
空间复杂度:O(1)。除了存储结果的列表,其他的变量空间都是常数级别的, 所以空间复杂度为O(1)。