三数之和
约 645 字大约 2 分钟
2025-06-25
问题
15.三数之和
给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为 0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
练习
三数之和
# your python code
思路
北海啊,要多想!
排序
将原数组nums
按升序排序。这样做有两个好处:- 可以用双指针(左右指针)在线性时间内寻找和为定值的两数之和;
- 相同元素集中在一起,便于跳过重复,避免结果去重的额外开销。
固定第一个数
nums[i]
遍历排序后数组的下标i
(只需到n-3
为止)。把nums[i]
作为三元组中的第一个元素,目标就是寻找 “两个数之和 = -nums[i]”。双指针寻找剩余两个数
在子数组nums[i+1..n-1]
中,使用左右指针j
、k
:- 初始时
l = i+1
,r = n-1
; - 计算
S = nums[i] nums[j] + nums[k]
:- 若
S > 0
,说明当前和偏大,需要减小,于是右指针左移k--
。 - 若
S < 0
,说明当前和偏小,需要增大,于是左指针右移j++
。 - 若
S == 0
,则找到一个解(nums[i], nums[l], nums[r])
,然后同时左右移动以避免重复。
- 若
- 初始时
跳过重复
- 外层去重:遍历
i
时,如果nums[i] == nums[i-1]
(且i>0
),就跳过,避免以相同值作为第一个元素再做一次扫描。 - 内层去重:当左右指针移动时,如果遇到与前一个位置相同的,也要直接跳过。
- 外层去重:遍历
Java
import java.util.*;
public class ThreeSum {
public static void main(String[] args) {
int[] nums = {-1,0,1,2,-1,-4};
// int[] nums = {0,1,1};
// Scanner scanner = new Scanner(System.in);
// int n = scanner.nextInt();
//
// int[] nums = new int[n];
// for (int i = 0; i < n; i++) {
// nums[i] = scanner.nextInt();
// }
System.out.println(threeSum(nums));
}
private static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = 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 j = i+1, k = n-1;
while (j < k) {
if (j > i+1 && nums[j] == nums[j-1]) {
j++;
continue;
} int S = nums[i] + nums[j] + nums[k];
if (S > 0) {
k--;
} else if (S < 0) {
j++;
} else {
ArrayList<Integer> triples = new ArrayList<>();
triples.add(nums[i]);
triples.add(nums[j]);
triples.add(nums[k]);
ans.add(triples);
j++;
k--;
}
}
}
return ans;
}
}
复杂度分析:
- 时间复杂度:O(N2)。
- 空间复杂度:O(log(N))。排序需要的额外空间是 O(log(N))