给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
看到本题,第一反应就是使用指针,接下来便是规定指针的变换,最初按照我的思路,结和之前几道题的经验,先sort()排序,然后在中间n-2项中选择某项作为中间项。于是排序好的数组按中间项分为了左右两个数组,后左右设置两个指针进行检测:
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums.sort()
res=[]
l=len(nums)
for i in range(1,l-1):
nums1=nums[:i]
nums2=nums[i+1:]
for j in nums1:
for k in nums2:
if j+k+nums[i]==0:
if [j,nums[i],k] not in res:
res.append([j,nums[i],k])
return res
问题是时间复杂度较高,初步执行了一下,对于例子中某个较长的列表,执行时间竟然有至少10min。
在查了一些资料后,得到一些化简的方式:
1.当 nums[i] > 0 时直接break跳出:因为 nums[k] >= nums[j] >= nums[i] > 0,即 3个数字都大于0 ,在此固定指针 i 之后不可能再找到结果了。
(max_v,min_v分别表示整个数组的最大,最小值)
2.if -(nums[i]+nums[j])>max_v: 直接continue
3.if -(nums[j]+nums[k])
https://leetcode-cn.com/problems/3sum/solution/3sumpai-xu-shuang-zhi-zhen-yi-dong-by-jyd/
即:将固定的数字由中间值改为将最小值固定,指针放在分割后右侧数组的右边进行寻找
这样会让检测的数组逐渐变小。而如果固定中间,则每次都要遍历数组,与暴力法相比虽然优化了很多,但依然运行时间较长,检测时间先增加后减小,呈中间峰值。
再加上之前优化的想法,于是有如下代码:
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if nums==[]:
return([])
nums.sort()
min_v=nums[0]
max_v=nums[-1]
res=[]
l=len(nums)
for i in range(l-2):
if nums[i]==nums[i-1] and i>0:
continue
elif nums[i]>0:
continue
nums2=nums[i+1:]
l2=len(nums2)
j=0
k=l2-1
while 1:
try:
if -(nums[i]+nums2[j])>max_v:
j+=1
else:
break
except:
break
while k>j:
sum1=nums[i]+nums2[j]+nums2[k]
if sum1>0:
k-=1
if nums2[k]<0:
continue
elif sum1<0:
j+=1
else:
if [nums[i],nums2[j],nums2[k]] not in res:
res.append([nums[i],nums2[j],nums2[k]])
k-=1
if nums2[k]<0:
continue
return res
时间以及大幅减少,但依然有8秒的运行时间,到底是哪里出了问题呢!!!!!!!!
https://leetcode-cn.com/problems/3sum/solution/3sumpai-xu-shuang-zhi-zhen-yi-dong-by-jyd/
经过对比我发现,区别在于检测数组是否已经存在浪费了巨大的时间
if [nums[i],nums2[j],nums2[k]] not in res:
res.append([nums[i],nums2[j],nums2[k]])
因为每次执行这句都会遍历整个res,在res较长的情况下会浪费巨量的时间。
*
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if nums==[]:
return([])
nums.sort()
max_v=nums[-1]
res=[]
l=len(nums)
for i in range(l-2):
if nums[i]==nums[i-1] and i>0:
continue
elif nums[i]>0:
break
j=i+1
k=l-1
while jmax_v:
j+=1
else:
break
while k>j and nums[k]>=0:
s=nums[i]+nums[j]+nums[k]
if s>0:
k-=1
elif s<0:
j+=1
else:
res.append([nums[i],nums[j],nums[k]])
k-=1
j+=1
while j < k and nums[j] == nums[j - 1]:
j += 1
while j < k and nums[k] == nums[k + 1]:
k -= 1
return res
经过这题对时间的概念加深了很多,应该尽量避免 in 等遍历操作,同时注意数据特点,在特定条件下及时直接跳入下一循环,减少明显多余的操作,降低时间,收获很大。
PS:貌似也可以用集合的形式来储存列表来避免元素重复,但列表不可哈希,因此是否可以用集合储存元组来试试呢?