LeetCode Hot100刷题——三数之和
15. 三数之和
1. 题目描述
给你一个整数数组 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
-10^5 <= nums[i] <= 10^5
2. 思路分析
- 暴力法:三重循环,时间复杂度为O(n^3),会超时,且去重麻烦
- 双指针法:先排序,然后固定一个数,再用双指针在固定数之后的区间寻找两个数,使得三数之和为0
排序的好处:
- 方便去重:相同的元素相邻,可以跳过重复元素避免重复解
- 双指针可以高效地寻找两数之和
具体步骤:
- 排序数组:首先对数组进行排序。排序后,相同的元素会相邻,这有助于后续跳过重复元素,避免重复的三元组。同时,排序使得我们可以使用双指针技巧高效地搜索三元组。
- 固定一个元素,使用双指针搜索:遍历数组,对于每个元素nums[i],使用双指针在 i+1 到数组末尾的区间内搜索两个元素 nums[left] 和 nums[right],使得三数之和为0。
- 双指针移动策略:
- 如果三数之和等于0,记录三元组,并移动左右指针跳过重复元素。
- 如果和小于0,左指针右移(增加和)
- 如果和大于0,右指针左移(减少和)
- 跳过重复元素:在移动指针时,跳过重复值,确保不会生成重复的三元组。
- 双指针移动策略:
- 跳过重复的固定元素:在遍历数组时,如果当前元素与前一个元素相同,则跳过,避免重复处理相同的三元组。
3. 解题过程
- 排序数组:使用Arrays.sort()对数组进行升序排序
- 初始化结果列表:用于存储所有满足条件的三元组
- 遍历数组:外层循环遍历每个元素nums[i]
- 跳过重复的nums[i]:如果当前元素与前一个元素相同,则跳过
- 双指针搜索:初始化左指针 left = i + 1 和右指针 right = nums.length - 1
- 计算三数之和 sum = nums[i] + nums[left] + nums[right]
- 根据sum的值移动指针:
- 如果sum == 0,记录三元组,移动左右指针并跳过重复元素。
- 如果sum < 0,左指针右移并跳过重复元素。
- 如果sum > 0,右指针左移并跳过重复元素。
- 返回结果:所有满足条件的三元组
程序代码
class Solution {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 (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){// 找到有效三元组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++;// 跳过重复的左指针元素while(left < right && nums[left] == nums[left - 1]){left++;}} else if(sum > 0){// 右指针左移right--;// 跳过重复的右指针元素while(left < right && nums[right] == nums[right + 1]){right--;}}} }return result;}
}
-
排序:通过排序,相同元素相邻,便于后续跳过重复元素。
-
外层循环:遍历每个元素作为固定元素,跳过重复值以避免重复三元组。
-
双指针:左指针从
i+1
开始,右指针从数组末尾开始,根据三数之和调整指针位置。 -
跳过重复元素:
-
在找到有效三元组后,移动左右指针并跳过重复值。
-
在移动左指针(和小于0时)或右指针(和大于0时)时,同样跳过重复值。
-
-
时间复杂度:排序
O(n log n)
,双指针搜索O(n^2)
,总体O(n^2)
。 -
空间复杂度:
O(1)
(不考虑存储结果的空间)。
补充
Arrays.asList()
是 Java 标准库中java.util.Arrays
类的一个静态方法,用于将一组元素转换为固定大小的列表(List
)
基本用法
import java.util.Arrays;
import java.util.List;public class Main {public static void main(String[] args) {// 创建包含多个元素的列表List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry");// 输出列表内容System.out.println(fruits); // [Apple, Banana, Cherry]}
}