题目
给你一个整数数组 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
思路
最直接的方法就是三重循环,但是时间复杂度为 O(n^3)
。考虑可以使用双指针法来优化,也就是确定第一个数之后,用双指针在同一个循环里查找另外两个数。而要使用双指针,肯定要先对数组进行排序,这样可以更方便地跳过重复的元素。代码如下:
1 | func threeSum(nums []int) [][]int { |
由于题目中表示不可以包含重复的三元组,且每个元素只能使用一次,所以在确定每一个元素时,都需要检查是否与前一个元素相同,如果相同则跳过。这样可以避免重复的三元组。
上面的代码中,首先对数组进行排序,然后使用一个循环来确定第一个数。在第一个循环里通过 if i > 0 && nums[i] == nums[i-1]
来跳过重复的元素。接着使用双指针法来查找另外两个数,双指针分别从当前数的下一个位置和数组的最后一个位置开始,计算三数之和。如果三数之和为 0
,则将三元组添加到结果中,并移动双指针跳过重复的元素。移动双指针时,也分别对左右指针使用 for
循环来跳过重复的元素,确保每个三元组都是唯一的。
题目
给你一个由 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]]
提示:
- 1 <= nums.length <= 200
- -109 <= nums[i] <= 109
- -109 <= target <= 109
思路
四数之和的思路与三数之和类似,也是使用双指针法。首先对数组进行排序,然后使用两个循环来确定前两个数,接着使用双指针法来查找另外两个数。代码如下:
1 | func fourSum(nums []int, target int) [][]int { |
仍然需要注意跳过重复的元素。首先对数组进行排序,然后使用两个循环来确定前两个数。在第一个循环里通过 if a > 0 && nums[a] == nums[a-1]
来跳过重复的 a
元素,第二个循环里通过 if b > a+1 && nums[b] == nums[b-1]
来跳过重复的 b
元素。接着使用双指针法来查找另外两个数,双指针分别从当前数的下一个位置和数组的最后一个位置开始,计算四数之和。如果四数之和等于目标值,则将四元组添加到结果中,并移动双指针跳过重复的元素。移动双指针时,也分别对左右指针使用 for
循环来跳过重复的元素,确保每个四元组都是唯一的。
上面的代码中还对一些情况做了裁剪:
- 当
nums[b+1]+nums[b+2] > rem
时,说明当前的b
位置已经无法找到合适的c
和d
了,直接跳出循环; - 当
nums[len(nums)-1]+nums[len(nums)-2] < rem
时,说明当前的b
位置也无法找到合适的c
和d
了,直接跳过当前的b
位置,继续下一个b
位置。
总结
这两个题,本质都是通过双指针来优化查找有序数组中满足条件的两个元素。而排序的时间复杂度为 O(log n)
,所以整体的时间复杂度相较于直接用嵌套循环查找两个元素的 O(n^2)
,是有很大提升的。
另外这两题都需要注意重复元素的跳过处理,以确保结果中不包含重复的三元组或四元组。通过对数组进行排序,可以更方便地跳过重复元素,从而提高算法的效率。