1-1
15.三数之和
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
Python most votes solution:
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = []
nums.sort()
for i in xrange(len(nums)-2):
if i > 0 and nums[i] == nums[i-1]:
continue
l, r = i+1, len(nums)-1
while l < r:
s = nums[i] + nums[l] + nums[r]
if s < 0:
l +=1
elif s > 0:
r -= 1
else:
res.append([nums[i], nums[l], nums[r]])
while l < r and nums[l] == nums[l+1]:
l += 1
while l < r and nums[r] == nums[r-1]:
r -= 1
l += 1; r -= 1
return res
时间复杂度:\(O(n^2)\)
分析:
-
首先排序,利用有序数组可以更快地找到想要的三元组;
-
第1个
if
判断从i=1
开始生效,它的作用是跳过重复的元素。例如考虑如下序列:nums = [-1, 2, 2, 2, 2, 3, 3, 4, 4, 4, 4, 6]
利用该
if
判断,程序会跳过前三个2,直接从第四个2开始运行if
判断后面的程序,因为题目中要求答案中不可以包含重复的三元组,若不这样做则会产生重复的三元组; -
l
和r
可以看做是左右两个指针,如果s < 0
,则左指针l
右移,使其指向更大的数;同理,如果s > 0
,则右指针r
左移,使其指向更小的数; -
如果
s = 0
,则说明找到了想要的三元组,此时第一步操作是先将它们放进res
中,因为有可能nums
中满足题意的三元组并非只有这一组,因此要继续做判断:-
在左指针依旧在右指针左侧的前提下,
(1)若左指针指向的当前位置的元素和它指向的下一个位置的元素(即左指针右移一位之后指向的元素)相等(此时就会出现题目中提到的“重复”的情况),因此要删除这种重复的情况。具体做法就是让左指针往右移一位就可以了。
(2)同理,若右指针指向的当前位置的元素和它指向的下一个位置的元素(即右指针左移一位之后指向的元素)相等,就又会出现重复的情况,删除这种重复情况的方法是让右指针左移一位。
(3)若终于左右指针均不再重复了(即下述
nums
序列中,左指针已经指向了从左往右数的最后一个2,右指针已经指向了从右往左数的最后一个4),则让左指针右移一位,同时右指针左移一位,继续在while
循环中做判断。nums = [-1, 2, 2, 2, 2, 3, 3, 4, 4, 4, 4, 6]
-
-
整个
for
循环总共要执行\(n-2\)次(i
的值从nums
中第一个元素nums[0]
一直遍历到倒数第二个元素nums[n-3]
; -
在每一次进入
while
循环之前,左右指针的位置都会被重新定向,使左指针指向nums[i]
右边紧邻的一个元素nums[i+1]
,右指针指向最后一个元素nums[n-1]
。整个while
循环会执行的次数为\[(n-1) - (i + 1) - 1 = n - i -3 \] -
因此,算法的整体时间复杂度为
\[O(n-2) \times O(n - i -3) \approx O(n^2) \]