1. 题目
给你一个整数数组 n u m s nums nums ,判断是否存在三元组 [ n u m s [ i ] , n u m s [ j ] , n u m s [ k ] ] [nums[i], nums[j], nums[k]] [nums[i],nums[j],nums[k]] 满足 i ≠ j 、 i ≠ k i \neq j、i \neq k i=j、i=k 且 j ≠ k j \neq k j=k ,同时还满足 n u m s [ i ] + n u m s [ j ] + n u m s [ k ] = 0 nums[i] + nums[j] + nums[k] = 0 nums[i]+nums[j]+nums[k]=0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:
n
u
m
s
=
[
−
1
,
0
,
1
,
2
,
−
1
,
−
4
]
nums = [-1,0,1,2,-1,-4]
nums=[−1,0,1,2,−1,−4]
输出:
[
[
−
1
,
−
1
,
2
]
,
[
−
1
,
0
,
1
]
]
[[-1,-1,2],[-1,0,1]]
[[−1,−1,2],[−1,0,1]]
解释:
n
u
m
s
[
0
]
+
n
u
m
s
[
1
]
+
n
u
m
s
[
2
]
=
(
−
1
)
+
0
+
1
=
0
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0
nums[0]+nums[1]+nums[2]=(−1)+0+1=0 。
n
u
m
s
[
1
]
+
n
u
m
s
[
2
]
+
n
u
m
s
[
4
]
=
0
+
1
+
(
−
1
)
=
0
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0
nums[1]+nums[2]+nums[4]=0+1+(−1)=0 。
n
u
m
s
[
0
]
+
n
u
m
s
[
3
]
+
n
u
m
s
[
4
]
=
(
−
1
)
+
2
+
(
−
1
)
=
0
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0
nums[0]+nums[3]+nums[4]=(−1)+2+(−1)=0。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:
n
u
m
s
=
[
0
,
1
,
1
]
nums = [0,1,1]
nums=[0,1,1]
输出:
[
]
[ ]
[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:
n
u
m
s
=
[
0
,
0
,
0
]
nums = [0,0,0]
nums=[0,0,0]
输出:
[
[
0
,
0
,
0
]
]
[[0,0,0]]
[[0,0,0]]
解释:唯一可能的三元组和为 0 。
提示:
- 3 < = n u m s . l e n g t h < = 3000 3 <= nums.length <= 3000 3<=nums.length<=3000
- − 1 0 5 < = n u m s [ i ] ≤ 1 0 5 -10^5 <= nums[i] \leq 10^5 −105<=nums[i]≤105
2. 思路及代码实现(Python)
2.1 排序 + 双指针
这道题目有点类似于《LeetCode_1_简单_两数之和》,当时题目当中是,如果确定了一个值,另一个值也能相应确定,因此可以用哈希表的形式存储值对,将时间复杂度降低为 O ( N ) O(N) O(N)。
而三数之和也类似的,我们可以视为第一个数确定后,后面两个数的搜索就变成了 “两数之和” 问题。对于两个数之和是固定值的问题,有一个特点是,当一个数增大,另一个加和数会减小,才能保证求和结果一致。因此这个特点非常适合在有序序列当中用双指针的方法,对此,基本思路确定,就是遍历一次数组,作为元组第一个数,另外两个数则用双指针的方式确定,在这个思路下,就需要对数组先进行排序。此外,题目中还提到找到的三元组不应重复,因此在固定第一个值前提下,移动变更第二个值时,若第二个值不变,则跳过。同理,在移动第一个值时,若第一个值移动后和移动前的值相等,则跳过。
此代码思路下,算法的复杂度为 O ( N 2 ) O(N^2) O(N2) 以及排序的复杂度 O ( N l o g N ) O(Nlog N) O(NlogN),加和结果也为 O ( N 2 ) O(N^2) O(N2)。空间复杂度为对数组的排序副本 O ( N ) O(N) O(N)。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
nums.sort()
ans = list()
# 枚举 a
for first in range(n):
# 需要和上一次枚举的数不相同
if first > 0 and nums[first] == nums[first - 1]:
continue
# c 对应的指针初始指向数组的最右端
third = n - 1
target = -nums[first]
# 枚举 b
for second in range(first + 1, n):
# 需要和上一次枚举的数不相同
if second > first + 1 and nums[second] == nums[second - 1]:
continue
# 需要保证 b 的指针在 c 的指针的左侧
while second < third and nums[second] + nums[third] > target:
third -= 1
# 如果指针重合,随着 b 后续的增加
# 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if second == third:
break
if nums[second] + nums[third] == target:
ans.append([nums[first], nums[second], nums[third]])
return ans
执行用时:589 ms
消耗内存:19.36 MB
参考来源:力扣官方题解