給定乙個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
解題思路例如, 給定陣列 nums =[-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為:
[ [-1, 0, 1],
[-1, -1, 2]
]
我們首先想到的解法是通過三重迴圈,於是我就寫出了如下**:
但是上面這個**是有問題的,因為我們沒有考慮結果重複的問題。接著我們想到可以通過collections.counter記錄所有數字出現的次數,如果前後有相同的話,我們就不新增到result中去。於是就有了下面的寫法class
solution:
defthreesum
(self, nums):
""" :type nums: list[int]
:rtype: list[list[int]]
"""result =
for i, a in enumerate(nums):
for j, b in enumerate(nums[i + 1:]):
for _, c in enumerate(nums[j + i + 2:]):
if a + b + c == 0:
return result
但是這種寫法的缺點很明顯,演算法的時間複雜度是o(n^3)這個級別的。我們能不能優化到o(n^2)這個級別呢?我們可以參考leetcode 1:兩數之和(最詳細解決方案!!!)文中的方法,通過乙個hash表來記錄nums中所有元素出現的次數。class
solution:
defthreesum
(self, nums):
""" :type nums: list[int]
:rtype: list[list[int]]
"""from collections import counter
k =
result =
for i, a in enumerate(nums):
for j, b in enumerate(nums[i + 1:]):
for _, c in enumerate(nums[j + i + 2:]):
if a + b + c == 0
and counter([a, b, c]) not
in k:
for i in k:
return result
當然這個演算法還有優化的空間,我們知道三個數和為0,那麼在三個數不全為0的情況下,必然有乙個正數和乙個負數,那麼我們可以通過兩個list去訪問nums中含有不重複元素的正數和負數。那樣我們就不用o(n^2)(n=len(nums))的時間,而只需要o(n*m)(n+m=len(nums))的時間複雜度。class
solution:
defthreesum
(self, nums):
""" :type nums: list[int]
:rtype: list[list[int]]
"""nums_hash = {}
result = list()
for num in nums:
nums_hash[num] = nums_hash.get(num, 0) + 1if0
in nums_hash and nums_hash[0] >= 3:
nums = sorted(list(nums_hash.keys()))
for i, num in enumerate(nums):
for j in nums[i+1:]:
if num*2 + j == 0
and nums_hash[num] >= 2:
if j*2 + num == 0
and nums_hash[j] >= 2:
dif = 0 - num - j
if dif > j and dif in nums_hash:
return result
另外我們還知道乙個條件,對於a,b,c三個數,如果a是正數,b是負數,那麼-c一定比b小,或者比a大。
例如:
所以我們可以這樣去解這個問題。a =
1b =-2
c =1-c=
-1a =3
b =-
2c =-1
-c=1同時也很好證明
-c= (a + b)即-c
- a = b a>=
0->
-c< b
-c= (a + b)即-c
- b = a b<
0->
-c> a
此處應有掌聲,非常好的解法是不是o(∩_∩)oclass
solution:
defthreesum
(self, nums):
""" :type nums: list[int]
:rtype: list[list[int]]
"""nums_hash = {}
result = list()
for num in nums:
nums_hash[num] = nums_hash.get(num, 0) + 1if0
in nums_hash and nums_hash[0] >= 3:
neg = list(filter(lambda x: x < 0, nums_hash))
pos = list(filter(lambda x: x>= 0, nums_hash))
for i in neg:
for j in pos:
dif = 0 - i - j
if dif in nums_hash:
if dif in (i, j) and nums_hash[dif] >= 2:
if dif < i or dif > j:
return result
另外這個問題我們也可以使用leetcode 167:兩數之和 ii - 輸入有序陣列(最詳細解決方案!!!)這篇文章中提到的對撞指標的思路。我們首先要將nums排序。
我們實際上只要考慮nums[i] <= 0的部分,因為當nums[i] > 0時,必然會造成nums[i], nums[l], nums[r]全部》0,這顯然不對。當i > 0時,我們要考慮nums[i - 1] == nums[i],如果成立,我們要跳出本次迴圈,執行++i,直到不成立為止。-4 -1 -101
2i l r
l = i+1
所以我們就有了如下的做法
class
solution:
defthreesum
(self, nums):
""" :type nums: list[int]
:rtype: list[list[int]]
"""result = list()
nums_len = len(nums)
if nums_len < 3:
return result
l, r, dif = 0, 0, 0
nums.sort()
for i in range(nums_len - 2):
if nums[i] > 0:
break
if i > 0
and nums[i - 1] == nums[i]:
continue
l = i + 1
r = nums_len - 1
dif = -nums[i]
while l < r:
if nums[l] + nums[r] == dif:
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
elif nums[l] + nums[r] < dif:
l += 1
else:
r -= 1
return result
兩數之和,三數之和
兩數之和 方法一 暴力 throw new illegalargumentexception 時間複雜度 o n 2 空間複雜度 o 1 public int twosum int nums,int target throw newillegalargumentexception no twosum...
兩數之和 三數之和 四數之和
兩數之和意思就是 給你乙個陣列,從中找出兩個數字,讓他們的和等於乙個具體的target。找到所有這樣的兩個數。並且這兩個數字不能完全一樣。n數之和的意思是 給你乙個陣列,從中找出n個數字,讓他們的和等於乙個具體的target。找到所有這樣的n個數。並且這n個數字不能完全一樣。最基礎的,也是最關鍵的就...
兩數之和,三數之和,最接近的三數之和,四數之和
二數之和 給定乙個整數陣列nums和乙個目標值target,請你在該陣列中找出和為目標值的那兩個整數,並返回他們的陣列下標。你可以假設每種輸入只會對應乙個答案。但是,你不能重複利用這個陣列中同樣的元素。示例 給定 nums 2,7,11,15 target 9 因為 nums 0 nums 1 2 ...