题目来自LeetCode,链接:4sum。具体描述为:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。注意:答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
这与之前一篇博客的三数之和很相似(没看的话需要先看一下),无非就是多了一层循环,之前用三个指针那这里就用四个指针罢了。
同样,首先对数组排序。然后初始化四个指针–first,second,third和fourth,让first遍历0到数组倒数第4个数,second遍历first+1到数组倒数第3个数,然后third就位于second+1,fourth就位于数组最后,让third和fourth进行相向运动(类似于三数之和的left和right)来寻找符合条件的四元组。具体描述如下:
这样虽然可以AC,但是会发现时间消耗还比较久,若想减少时间需要做一些判断:
可以看到这种方法的时间复杂度为 O ( n 3 ) O(n^{3}) O(n3)。
JAVA版代码如下:
class Solution {
public List> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List> result = new ArrayList<>();
int numsLen = nums.length;
int first = 0;
while (first < numsLen - 3) {
int second = first + 1;
if (nums[first] + nums[first+1] + nums[first+2] + nums[first+3] > target) {
break;
}
if (nums[first] + nums[numsLen-3] + nums[numsLen-2] + nums[numsLen-1] < target) {
++first;
while (first < numsLen - 3 && nums[first] == nums[first - 1]) {
++first;
}
continue;
}
while (second < numsLen - 2) {
if (nums[first] + nums[second] + nums[second+1] + nums[second+2] > target) {
break;
}
if (nums[first] + nums[second] + nums[numsLen-2] + nums[numsLen-1] < target) {
++second;
while (second < numsLen - 2 && nums[second] == nums[second - 1]) {
++second;
}
continue;
}
int third = second + 1;
int fourth = numsLen - 1;
while (third < fourth) {
int sum4num = nums[first] + nums[second] + nums[third] + nums[fourth];
if (sum4num == target) {
result.add(Arrays.asList(nums[first], nums[second], nums[third], nums[fourth]));
++third;
while (third < fourth && nums[third] == nums[third - 1]) {
++third;
}
--fourth;
while (fourth > third && nums[fourth] == nums[fourth + 1]) {
--fourth;
}
}
else if (sum4num < target) {
++third;
while (third < fourth && nums[third] == nums[third - 1]) {
++third;
}
}
else {
--fourth;
while (fourth > third && nums[fourth] == nums[fourth + 1]) {
--fourth;
}
}
}
++second;
while (second < numsLen - 2 && nums[second] == nums[second - 1]) {
++second;
}
}
++first;
while (first < numsLen - 3 && nums[first] == nums[first - 1]) {
++first;
}
}
return result;
}
}
提交结果如下:
Python版代码如下:
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
length = len(nums)
result = []
nums.sort() #先排序
first = 0
while first < length - 3: #第一个数最右的位置也只能到倒数第四
second = first + 1
if sum(nums[first : first + 4]) > target: #最小值已经大于target,可以结束
return result
if nums[first] + sum(nums[length - 3 : length]) < target:
first += 1
while first < length - 3 and nums[first] == nums[first - 1]:
first += 1 #将第一个数的索引移动到下一个不一样的数上
continue #最大值已经小于target,必须再增大第一个数
while second < length - 2: #第二个数最右的位置也只能到倒数第三
if nums[first] + sum(nums[second : second + 3]) > target:
break #最小值已经大于target,跳出第二层循环
if nums[first] + nums[second] + sum(nums[length - 2 : length]) < target:
second += 1
while second < length - 2 and nums[second] == nums[second - 1]:
second += 1 #将第二个数的索引移动到下一个不一样的数上
continue #最大值小于target,必须再增大第二个数
third = second + 1
fourth = length - 1
sum12 = nums[first] + nums[second]
while third < fourth:
sum4num = sum12 + nums[third] + nums[fourth]
if sum4num == target: #如果四个数之和刚好等于目标值,加入结果list
result.append([nums[first], nums[second], nums[third], nums[fourth]]);
third += 1
while third < fourth and nums[third] == nums[third - 1]:
third += 1 #将第三个数的索引移动下一个不一样的数上去
fourth -= 1
while fourth > third and nums[fourth] == nums[fourth + 1]:
fourth -= 1 #将第四个数的索引移动下一个不一样的数上去
elif sum4num < target : #当四个数之和小于目标值,第三个数的索引右移到一个不同的数上
third += 1
while third < fourth and nums[third] == nums[third - 1]:
third += 1
else: #当四个数之和大于目标值,第四个数的索引左移到一个不同的数上
fourth -=1
while fourth > third and nums[fourth] == nums[fourth + 1]:
fourth -= 1
second += 1
while second < length - 2 and nums[second] == nums[second - 1]:
second += 1 #将第二个数的索引移动到下一个不一样的数上
first += 1
while first < length - 3 and nums[first] == nums[first - 1]:
first += 1 #将第一个数的索引移动到下一个不一样的数上
return result
提交结果如下: