一、三数之和
题目:
给你一个整数数组 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 。
思路:
本题的关键并不是如何寻找a+b+c=0,而是需要判断在找到符合要求的三元组后应该如何去重,三个元素均需要进行去重操作,不然很容易造成错误,以代码具体进行分析
首先,对数组进行排序
Arrays.sort(nums);
初始化结果列表
List<List<Integer>> result = new ArrayList<>();
遍历数组并判断特殊情况
if (nums[i] > 0) {
return result;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
如果当前位置 i 对应的元素 nums[i] 大于 0,由于数组已经排序,说明后续元素都大于 0,不存在三数之和为 0 的情况,直接返回结果 result
如果 i > 0(确保不越界)并且当前元素 nums[i] 等于上一个元素 nums[i - 1],则跳过当前循环,避免重复计算相同的三元组。
使用双指针进行查找
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum < 0) {
left++;
} else {
// 处理找到的三元组
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 处理重复元素
while (right > left && nums[right] == nums[right - 1])
right--;
while (right > left && nums[left] == nums[left + 1])
left++;
left++;
right--;
}
}
用双指针法,left 指向 i 的下一个位置,right 指向数组末尾。在 left < right 的条件下,计算当前三个元素的和 sum:
如果 sum > 0,则将 right 指针向左移动;
如果 sum < 0,则将 left 指针向右移动;
如果 sum == 0,则找到了一组满足条件的三元组,加入到 result 中,并且跳过重复的元素,继续寻找下一组。
最后返回结果列表
return result;
完整代码:
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
return result;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum < 0) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (right > left && nums[right] == nums[right - 1])
right--;
while (right > left && nums[left] == nums[left + 1])
left++;
left++;
right--;
}
}
}
return result;
}
asList方法介绍:
当你使用 Arrays.asList 方法时,你可以将一个数组转换成一个固定大小的列表。例如:
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
// 创建一个数组
String[] array = {"苹果", "香蕉", "樱桃"};
// 将数组转换为列表
List<String> list = Arrays.asList(array);
// 打印列表
System.out.println("列表内容: " + list);
}
}
在这个例子中:
我们有一个包含字符串的数组 array。
我们使用 Arrays.asList(array) 将数组转换为一个 List<String>。
得到的 list 是一个固定大小的列表,它与原始数组关联,这意味着对列表的任何修改都会反映在原始数组中,反之亦然。
打印 list 将显示它的内容。
这个方法在需要快速将数组转换为列表或者在需要使用 List 接口提供的方法对数组进行操作时非常有用。记住 Arrays.asList 返回的列表是固定大小的,意味着你不能直接向其中添加或移除元素(但可以修改现有元素)。
二、四数之和
题目:
给你一个由 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]]
思路:
与三数之和不同的是,该题需要实现a+b+c+d=target,但整体思路与三数之和雷同,因此需要实现许多地方的优化,即对代码进行剪枝操作,避免造成不必要的遍历而增加代码的执行时间
代码:
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
//剪枝操作
//这个条件首先检查当前索引 i 处的 nums[i] 是否大于 0,并且大于 target。
//如果满足这个条件,意味着数组 nums 已经按照升序排序,当前 nums[i] 及其后的所有元素都大于 target。
//因此,没有可能存在四个数的组合使得它们的和等于 target,所以直接返回当前的结果 result。可以提前结束搜索,节省时间。
if (nums[i] > 0 && nums[i] > target) {
return result;
}
//去重操作
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.length; j++) {
//去重操作
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int left = j + 1;
int right = nums.length - 1;
while (left < right) {
//int型会数据溢出
long sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum > target) {
right--;
} else if (sum < target) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1])
left++;
while (left < right && nums[right] == nums[right - 1])
right--;
left++;
right--;
}
}
}
}
return result;
}
比较:
相同点:
目标:都是在数组中找到满足特定条件的数的组合,使得它们的和等于给定的目标值。
解题思路:通常都使用排序和双指针的方法来解决。首先对数组排序,然后通过双指针技巧来找到满足条件的数值组合。
优化策略:都可以利用排序后的数组特性进行优化,比如跳过重复值以及提前终止搜索等。
不同点:
数的个数:
三数之和:要求找到数组中三个数的组合,使它们的和等于目标值。
四数之和:要求找到数组中四个数的组合,使它们的和等于目标值。
时间复杂度:
三数之和:在最坏情况下,使用排序后的双指针法,时间复杂度为 O(n^2),其中 n 是数组的长度。
四数之和:在最坏情况下,时间复杂度也是 O(n^3),因为需要嵌套两层循环来处理四个数的组合。
实现复杂性:
四数之和相对于三数之和来说,在代码实现上会更加复杂,因为需要多一层循环来处理第四个数的选择和组合。
结果去重:
四数之和问题需要特别注意如何避免得到重复的四元组。这通常需要在找到一个满足条件的四元组后,跳过相同的元素以及处理重复组合的情况,以确保结果的唯一性。
总的来说,四数之和相较于三数之和在解题思路和复杂度上有一定的相似性,但也有明显的区别,特别是在处理结果去重和时间复杂度的控制上需要更多的考虑。
今天的学习就到这里了