💬先看原题:
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
🤪代码思路
直接给出代码思路,该思路使用双指针的方法来遍历数组:
- 首先对数组进行排序,以便于后续双指针遍历。排序的时间复杂度为 O(nlogn),其中 n 是数组的长度。
- 初始化一个空结果列表 result。
- 对数组进行遍历,遍历索引 i,从 0 到 n-3(n 是数组的长度)。在遍历过程中,如果当前元素 nums[i] 与前一个元素相等,则跳过,以避免重复解。
- 在每个固定的 nums[i] 下,使用双指针 left 和 right 来寻找满足条件的三元组。left 初始化为 i+1,right 初始化为 n-1。
- 循环移动 left 和 right 指针来搜索满足条件的三元组:
- 如果 nums[i] + nums[left] + nums[right] 等于 0,则将 [nums[i], nums[left], nums[right]] 添加到结果列表 result 中,同时将 left 和 right 向内移动,跳过所有重复的元素。
- 如果 nums[i] + nums[left] + nums[right] 小于 0,则将 left 右移一位。
- 如果 nums[i] + nums[left] + nums[right] 大于 0,则将 right 左移一位。
- 继续执行步骤 4 和步骤 5,直到 left 和 right 相遇为止。
- 返回结果列表 result。
整体的时间复杂度为 O(n^2),其中 n 是数组的长度。
👾完整代码
package com.example.codebin.LeetCode100;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ThreeSum {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
int n = nums.length;
Arrays.sort(nums);
for (int i = 0; i < n - 2; i++) {
// 跳过重复解
if (i > 0 && nums[i] == nums[i - 1]) {
continue; //重复则直接跳出
}
int left = i + 1;
int right = n - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
left++;
right--;
// 跳过重复解
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return result;
}
public static void main(String[] args) {
int[] nums = {-1, 0, 1, 2, -1, -4};
ThreeSum solution = new ThreeSum();
List<List<Integer>> result = solution.threeSum(nums);
for (List<Integer> triplet : result) {
System.out.println(triplet);
}
}
}
💭代码解析
1、初始化一个空结果列表 result。
List<List<Integer>> result = new ArrayList<>();
List<List<Integer>>
表示一个二维列表,外层的 List
存储所有的解,内层的 List<Integer>
存储每个解对应的三个数。
2、首先对数组进行排序,以便于后续双指针遍历。排序的时间复杂度为 O(nlogn),其中 n 是数组的长度。
Arrays.sort(nums); // 是 Java 中的一个静态方法,用于对数组进行排序。
Arrays
类属于 java.util
包,因此需要使用 import java.util.Arrays;
语句引入该类。
3、对数组进行遍历,遍历索引 i,从 0 到 n-3(n 是数组的长度)。在遍历过程中,如果当前元素 nums[i] 与前一个元素相等,则跳过,以避免重复解。
for (int i = 0; i < n - 2; i++) {
// 跳过重复解
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
//在每个固定的 nums[i] 下,使用双指针 left 和 right 来寻找满足条件的三元组
int left = i + 1;
int right = n - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
left++;
right--;
// 跳过重复解
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
} else if (sum < 0) {
//当三个数的和 `sum` 小于 0 时,意味着当前的和偏小。
//由于数组是有序的(根据算法前提),为了增加和的值,需要将左指针 `left` 向右移动一位。
left++;
} else {
//反之,同理。
right--;
}
}
}
return result;
}
n-3
这是因为在计算三个数的和时,需要至少留出两个位置给左右指针。
使用 for
循环遍历数组 nums
,其中 i
表示当前选取的第一个数。内部使用双指针 left
和 right
分别指向剩余部分的起始和结束位置。
在内部的 while
循环中,计算当前三个数的和 sum
,然后根据 sum
的值进行处理:
- 如果
sum
等于 0,则将这三个数添加到结果集result
中,并同时将left
和right
指针向内移动一位。 - 如果
sum
小于 0,则说明和偏小,需要将left
指针向右移动一位。 - 如果
sum
大于 0,则说明和偏大,需要将right
指针向左移动一位。
在 sum
等于 0 的情况下,内部还有两个 while
循环用于跳过重复的解。