leetcode
leetcode 1 ~ 50
四数之和

四数之和

难度:

标签:

题目描述

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • 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

代码结果

运行时间: 1324 ms, 内存: 15.3 MB


/*
* 思路:
* 1. 首先对数组进行排序。
* 2. 使用组合算法生成四元组,过滤掉不符合条件的四元组。
* 3. 使用Stream API的distinct()方法去除重复的四元组。
* 4. 将结果收集为List。
*/
 
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
public class FourSumStream {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        return IntStream.range(0, nums.length)
                .boxed()
                .flatMap(a -> IntStream.range(a + 1, nums.length)
                        .boxed()
                        .flatMap(b -> IntStream.range(b + 1, nums.length)
                                .boxed()
                                .flatMap(c -> IntStream.range(c + 1, nums.length)
                                        .boxed()
                                        .filter(d -> nums[a] + nums[b] + nums[c] + nums[d] == target)
                                        .map(d -> Arrays.asList(nums[a], nums[b], nums[c], nums[d]))
                                )
                        )
                )
                .distinct()
                .sorted((o1, o2) -> {
                    for (int i = 0; i < o1.size(); i++) {
                        int cmp = o1.get(i) - o2.get(i);
                        if (cmp != 0) return cmp;
                    }
                    return 0;
                })
                .collect(Collectors.toList());
    }
}
 

解释

方法:

这道题目使用了nSum的递归思想来解决。首先将数组排序,然后递归地调用nSum函数,将问题不断分解成更小的子问题,直到问题简化为两数之和。在两数之和的处理中,使用双指针技巧来寻找和为目标值的元素对。为了避免重复解,每次找到一个解后,需要跳过相同的元素。

时间复杂度:

O(n^3)

空间复杂度:

O(n^3)

代码细节讲解

🦆
为什么在两数之和的问题中选择使用双指针而不是哈希表的方法?
在排序数组中,使用双指针方法可以更直观且高效地找到和为目标值的两个数。使用双指针时,可以有效地利用数组的有序性,通过移动左右指针以逼近目标值,从而避免额外的空间复杂度。而使用哈希表虽然能在未排序的情况下快速查找元素,但它需要额外的空间来存储元素和它们的索引,且在处理重复元素和保持结果的有序性方面更加复杂。
🦆
在多层递归中如何确保避免重复的四元组出现?
为了确保不出现重复的四元组,算法在每次找到有效的子问题解后,会跳过所有与当前元素相同的后续元素。这是通过在递归的每一层中,在处理完当前元素并找到所有相关的子问题解之后,检查后续元素是否与当前元素相同来实现的。如果相同,就继续跳过,直到找到一个不同的元素或者数组结束。这种方法确保每个数字在每个位置只被使用一次,从而避免生成重复的组合。
🦆
当nSum函数中的n大于2时,为什么选择将问题递减至n-1而不是更小的数?
递归地将nSum问题递减至n-1是为了简化问题的复杂性,同时保持问题的结构清晰和可控。通过逐步递减,可以将n数之和的问题逐步分解为更易于解决的子问题,最终简化到两数之和,这是一个直接可以通过双指针高效解决的问题。如果直接跳过n-1到更小的数,比如n-2,虽然理论上可行,但在实践中会使问题的分解和解决变得复杂,需要更多的递归层次和复杂的中间状态管理。

相关问题

两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

 

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

 

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

 

进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?

三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != 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

四数相加 II

给你四个整数数组 nums1nums2nums3nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

 

示例 1:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

示例 2:

输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1

 

  提示:

  • n == nums1.length
  • n == nums2.length
  • n == nums3.length
  • n == nums4.length
  • 1 <= n <= 200
  • -228 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 228