給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
例如, 給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]
面試中 優先考慮雙指針,並使用set保存res,即可以不考慮去重
思路一:利用twoSum
遍歷nums,然後轉換爲twoSum問題,但是要排除重複結果,判重操作時間O(n),twoSum本身時間O(n),空間O(n),總體時間複雜度O(n^3),空間複雜度O(n)
class Solution(object): # 此法也超時
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
def twoSum(nums, target):
lookup = {}
for num in nums:
if target - num in lookup:
if (-target ,target - num, num) not in res:
res.append((-target ,target - num, num))
lookup[num] = target - num
n = len(nums)
nums.sort()
res = []
for i in range(n):
twoSum(nums[i+1:], 0-nums[i])
return [list(i) for i in res]
思路二:暴力法
先給nums排序,滿足結果從小到大排列,三層循環
class Solution(object):
def threeSum(self, nums):
n = len(nums)
res = []
nums.sort()
for i in range(n):
for j in range(i,n):
for k in range(j,n):
if nums[i] + nums[j] + nums[k] == 0 and j != i and k != j and k != i:
curRes = [nums[i],nums[j],nums[k]]
if curRes not in res:
res.append(curRes)
return res
思路三:優化二
兩個for loop枚舉a,c,則b=-a-c,然後去dyct中查詢-a-c是否存在。利用set保存res,達到去重效果。
整體時間複雜度O(n^2),空間複雜度O(n)
def threeSum(nums):
if len(nums) < 3:
return []
nums.sort()
res = set()
for i,a in enumerate(nums[:-2]):
if i >= 1 and a == nums[i-1]:
# 兩個連續的值相等時,已經處理過了,跳過此次循環,
continue
if v > 0: break ##因爲已經排序了,所以到大於零的位置,之後的數字都是大於零的,肯定沒有符合條件的組合了
dyct = {}
for c in nums[i+1:]:
if c not in dyct:
dyct[-a-c] = None
else:
res.add((a, -a-c, c))
return list(map(list, res))
threeSum([-1, 0, 1, 2, -1, -4])
[[-1, -1, 2], [-1, 0, 1]]
思路四:雙指針
排序好nums,遍歷nums,對nums[i+1:],使用兩個指針l,r分別從左右開始,計算當前數nums[i]和nums[l],nums[r]之和s,若s小於0,由於nums是排好序的,只需l右移,若s大於0則只需r左移,若等於0,則將nums[i],nums[l],nums[r]存入res列表,並左移r 右移l 繼續比較。直至兩個指針發生碰撞。
注意點:對於相鄰重複的元素,需要跳過。
由於需要遍歷nums,且雙指針遍歷nums[i+1:],則總體時間複雜度爲O(n^2),空間複雜度爲O(n)
def threeSum(nums):
'''
使用set保存res,不需要考慮判重,簡單直接,思路清晰
'''
res = set()
nums.sort()
for i in range(len(nums)-2):
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.add((nums[i],nums[l],nums[r]))
l += 1
r -= 1
return [list(i) for i in res]
threeSum([-1, 0, 1, 2, -1, -4])
[[-1, -1, 2], [-1, 0, 1]]
threeSum([0,0,0,0,0,0,0,0,0])
[[0, 0, 0]]
充分考慮nums結構,進行判重,減少操作,減少時間,加大代碼複雜度
且利用排序好的nums,可知當nums[i]>0時,i,i+1,i+2三個元素都大於零,即i以後的元素不存在解。
def threeSum(nums):
'''
使用list保存res,在過程中判重。
'''
res = []
nums.sort()
for i in range(len(nums)-2):
if i>0 and nums[i] == nums[i-1]: # 兩個連續的值相等時,已經處理過了,跳過此次循環,
continue
if nums[i]>0: break #因爲已經排序了,所以到大於零的位置,之後的數字都是大於零的,肯定沒有符合條件的組合了
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
threeSum([-1, 0, 1, 2, -1, -4])
[(-1, -1, 2), (-1, 0, 1)]
執行用時 : 844 ms, 在3Sum的Python3提交中擊敗了88.77% 的用戶
內存消耗 : 16.4 MB, 在3Sum的Python3提交中擊敗了97.63% 的用戶