LeetCode算法日记 - Day 4: 三数之和、四数之和
1. 三数之和
15. 三数之和 - 力扣(LeetCode)
给你一个整数数组 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 。
1.1 题目解析
解决该问题可以用排序+对撞指针解决,解决问题的可以分两种:暴力枚举 和 优化之后的算法。
a)暴力枚举可以通过三层 for 循环+ set 集合去重的方法解决,不过大概率面临 “迷失” 的风险。
b)我们可以通过先排序来使整个数组变为有序数组,然后通过固定一个数 a 再定义额外俩指针,使这俩指针的数 和 a 的绝对值相同。因为只有 a 为负数的时候才面临三数相加等于 0,所以当 a 大于 0 时就可以返回数组了。
这个解法的核心就是不用 set 去重,要面临三重去重的问题,下面我来详细解答。
1.2 解法
i)排序
ii)固定数 a
iii)在这个数后面的区间内,使用 [双指针算法] 快速找到两个数之和等于 -a 。
iiii)当找到一个结果之后,left 和 right 配合 while 循环跳过重复的元素。
iiiii)使用完一次双指针之后,对固定的 a 也要使用 while 循环跳过重复的元素。
1.3 代码实现
class Solution {public List<List<Integer>> threeSum(int[] nums) {Arrays.sort(nums);int n = nums.length;List<List<Integer>> ret = new ArrayList<>();for(int i=0;i<n-2;){if(nums[i]>0){break;}int left = i+1, right = n-1, target = -nums[i]; while(left<right){int sum = nums[right] + nums[left];if(sum < target){left++;}else if(sum>target){right--;}else{ret.add(new ArrayList<Integer>(Arrays.asList(nums[i],nums[left],nums[right])));left++;right--;while(nums[left] == nums[left-1] && left<right){left++;}while(nums[right] == nums[right+1]&& right>left){right--;}}}i++;while(i<n-2 &&nums[i] == nums[i-1]){i++;}}return ret;}
}
2. 四数之和
18. 四数之和 - 力扣(LeetCode)
给你一个由 n
个整数组成的数组 nums
,和一个目标值 target
。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a
、b
、c
和d
互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]
2.1 题目解析
四数之和与三数之和的解决办法如出一辙,都可以通过先排序,再双指针来解决问题。固定一个数 a ,在 a 之后的区间内再固定一个数 b,在 b 之后的区间内使用双指针即可解决。不过需要注意的是,要对数 a 也就是最外层的数也要进行去重。
2.2 解法
i)排序
ii)固定数 a
iii)固定数b。
iii)在数 b 后面的区间内,使用 [双指针算法] 快速找到两个数之和等于 -a-b 。
iiii)当找到一个结果之后,left 和 right 配合 while 循环跳过重复的元素。
iiiii)使用完一次双指针之后,对固定的 b 也要使用 while 循环跳过重复的元素。
iiiiii)最后对数 a 进行去重,使用 while 循环跳过重复的元素。
2.3 代码实现
class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {Arrays.sort(nums);int n = nums.length;List<List<Integer>> ret = new ArrayList<>();for(int i = 0; i<n-3;){for(int j =i+1;j<n-2;){long dest = ((long)target-(long)nums[i]-(long)nums[j]);int left = j+1, right = n-1;while(left<right){long sum = (long)nums[left] + (long)nums[right];if(sum<dest){left++;}else if(sum > dest){right--;}else{ret.add(new ArrayList<Integer>(Arrays.asList(nums[i],nums[j],nums[left],nums[right])));left++;right--;while(left<right&&nums[left]==nums[left-1]){left++;}while(left<right&&nums[right] == nums[right+1]){right--;}}}j++;while(j<n-2&&nums[j]==nums[j-1]){j++;}}i++;while(i<n-3&&nums[i] == nums[i-1]){i++;}}return ret;}
}