LeetCode15三數之和
題目描述:
給定一個包含 n 個整數的陣列 nums
,判斷 nums
中是否存在三個元素 *a,b,c ,*使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。
**注意:**答案中不可以包含重複的三元組。
例如, 給定陣列 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為:
[
[-1, 0, 1],
[-1, -1, 2]
]
解題思路:
這題肯定要寫迴圈遍歷陣列的。
-
想到的第一種辦法就是:遍歷陣列,每次都與其後面的陣列進行相加,如果得到的結果的倒數,在剩餘陣列中能夠找到,則成功。
存在的問題:
-
如何快速的定位兩數之和的倒數仍在剩餘的陣列中
-
如果有重複的結果,如何有效的去重
第一個問題,我採用的辦法是使用
python list
中的in
方法,因為list是使用線性結構儲存的,其查詢複雜度是 ,整個程式的時間複雜度就達到了 。列表與字典的儲存結構不同,字典使用Hash表結構來儲存,其存取效率要高很多,時間複雜度是 。第二個問題,去重也是使用的
in
方法判斷是否在列表其中,在其中,則不新增。if len(nums) < 3: return [] result = [] for i in range(len(nums)-1): for j in range(i+1, len(nums)): sum = nums[i] + nums[j] if -sum in nums: k = nums.index(-sum) if k != i and k != j: temp = sorted([nums[i], nums[j], nums[k]]) if temp not in result: result.append(temp) return result
-
-
所以想的是,為了提高遍歷效率,要考慮使用雙指標。
Tips : 一般遍歷問題,都考慮一下是否可以使用雙指標,然後想清楚雙指標的移動條件。
先對陣列排序,使用雙指標從首尾兩端對陣列進行遍歷。每次都得到首尾兩端數字的和,然後判斷兩數字和的倒數是否在剩餘的列表切片中即可。如果在的話,則加到結果集中。如果不在的話… 額(⊙o⊙)…是個問題。
如果兩數字之和大於0,則需要右邊(大於0)的指標左移
如果兩數之和小於0…
好像這樣講不出道理。。。GG
-
雖然上一種做法有問題,不過雙指標的想法還是正確的。所以,應該換個思路。
先對列表進行排序,然後從頭到尾遍歷。這樣每次都固定住一個值,而不是用雙指標來解決三值問題,好像是可行的。
在固定一個值之後,剩下的有序列表中,用雙指標法尋找兩個值的和是否是固定值的負數。這樣指標移動的標準也就出來了。如下圖:
- 當兩值之和大於固定值的負數時,右邊的指標左移
- 當兩值之和小於固定值的負數時,左邊指標右移
- 當兩值之和恰好為固定值負數,儲存結果。此時有個很有必要的操作:判斷 start+1的值,end-1的值,是否與上一個一樣,相同的話,則跳過處理,我一開始就沒處理,想偷懶最後儲存結果的時候去重即可,但是去重要使用
in
方法, 的複雜度,增加了時間複雜性。
解答:
實現是使用的Python3寫的。程式碼地址在GitHub,可除錯:https://github.com/zhangdianlei/LeetCode_python/blob/master/src/c15.py
實現:
if len(nums) < 3:
return []
result = []
nums = sorted(nums)
for index, item in enumerate(nums):
target = -item
start = index + 1
end = len(nums) - 1
if item > 0 or nums[end] < 0:
break
if index > 0 and item == nums[index-1]:
continue
while start < end:
if nums[end] < 0:
break
if nums[start] + nums[end] == target:
temp_list = [nums[index], nums[start], nums[end]]
result.append(temp_list)
while start < end and nums[start] == nums[start+1]:
start = start + 1
while start < end and nums[end] == nums[end-1]:
end = end - 1
start = start + 1
end = end - 1
elif nums[start] + nums[end] < target:
start = start + 1
else:
end = end - 1
return result