题目在leetcode上的链接为:
https://leetcode-cn.com/problems/3sum/
题目描述
解题思路
首先想到的是使用暴力法解题,使用三重循环遍历数组,然后判断三个数相加是否为0并去重即可,时间复杂度为 o(n3)。为了降低时间复杂度,我们可以将数组排序后再使用双指针的解法。
具体步骤为:
- 首先如果 nums 为 None,或者 nums 长度小于3,那么返回空列表
- 将 nums 按照从小到大进行排序
- 使用变量 i 循环遍历 nums:
- 令左指针 left 为 i+1,右指针 right 为 len(nums)-1
- 如果 nums[i]>0,说明三个数中最小的都大于0,那么之后就不存在三个数之和等于0了,直接返回结果
- 如果 nums[i]=nums[i-1],说明前一步中已经考虑了这一步可能存在的三个数的结果,那么就不执行之后的代码,以此进行去重操作
- 以 left<right 作为判断条件进入循环:
- 如果 nums[i]+nums[left]+nums[right] 等于0:
- 将找到的三个数添加到结果中
- 循环判断如果左右指针位置的数等于它门下一个位置的数时,就将指针移到下一个位置进行去重操作
- 将左右指针都移到下一个位置重新找下一个可能的结果
- 如果 nums[i]+nums[left]+nums[right] 小于0,说明太小了,则将左指针向右移动一步
- 如果 nums[i]+nums[left]+nums[right] 大于0,说明太大了,则将右指针向左移动一步
- 如果 nums[i]+nums[left]+nums[right] 等于0:
上述解法中容易出错的就是去重操作,需要仔细注意下两方面的去重操作,一个是遍历 i 的过程中可能出现重复,另一个是在固定 i 时,左右指针遍历 i 之后的数可能出现重复:
(1)当 nums[i]=nums[i-1] 时,由于在 i-1 的步骤中左右指针进行遍历操作时,已经遍历过了 i-1 右边的数了,因此在 i 的位置处找到的符合条件的三个数肯定已经包含在 i-1 位置处找到的结果中,因此要进行去重操作。
(2)另外就是在之后的 nums[i]+nums[left]+nums[right] 等于0 时,这个时候 i 是固定的,如果 nums[left]=num[left+1],说明左指针下一个位置的结果与当前的结果重复;同理如果 nums[right]=num[right-1],说明右指针下一个位置的结果与当前的结果重复,需要进行去重操作
复杂度分析:
排序操作的时间复杂度为 o(nlog(n)),两重循环的复杂度为 o(n2),由于这两步操作是顺序执行,所以总的时间复杂度为 o(nlog(n))+o(n2),即为 o(n2)。
由于只需要常数级的存储空间,所以空间复杂度为 o(1)
python代码:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if not nums or len(nums) < 3:
return []
nums.sort()
res = []
for i in range(len(nums)):
if nums[i] > 0:
return res
if i > 0 and nums[i] == nums[i - 1]:
continue
left = i + 1
right = len(nums) - 1
while left < right:
if nums[i] + nums[left] + nums[right] == 0:
res.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right -1]:
right -= 1
left += 1
right -= 1
elif nums[i] + nums[left] + nums[right] < 0:
left += 1
else:
right -= 1
return res