Leetcode 018 四數之和 思路詳解+反思易錯 Python實現
本人一直在努力地積累Leetcode上用Python實現的題,並且會盡力講清每道題的原理,絕不像其他某些部落格簡略地帶過。
如果覺得講的清楚,歡迎關注。
給定一個包含 n 個整數的陣列 nums
和一個目標值 target
,判斷 nums
中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target
相等?找出所有滿足條件且不重複的四元組。
注意:
答案中不可以包含重複的四元組。
示例:
給定陣列 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 滿足要求的四元組集合為: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
思路:很明顯。四數之和與三數之和很像,但當我用四指標夾逼來做時發現超時了,但卻有大神寫的能通過。這說明就算是相同的演算法,程式碼的冗雜程度也會很大程度上影響效能。所以說夾逼遍歷,最重要的就是過濾!過濾!過濾!怎樣把一定不符合條件的值跳過才是最能提升效能的。
但是先來看看我提交的程式碼:
1.字典查詢法
總思路:把N4拆成2個N2。第一個for迴圈,先求後2個值可能的取值的所有情況,並且把它們儲存在一個字典裡,以和作為鍵。
第二個for,我們遍歷前2個值所可能取的各種值,算出和並且檢查target - onesum是否在我們的字典裡,如果在,就說明我們找到了一個解。其實這種求幾數之和的問題,都可以通過這種思路。比如說三數之和,現在我就想到根本不必用指標,算出所有後2個值所加的可能的和,然後用字典存,最後用target-第一個值去檢查是否存在這樣的鍵。
class Solution: def fourSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[List[int]] """ nums.sort() ans = set() sumans = {} if len(nums) < 4: return [] for i in range(2, len(nums) - 1): for j in range(i+1, len(nums)): onesum = nums[i] + nums[j] if onesum not in sumans: sumans[onesum] = [(i, j)] else: sumans[onesum].append((i, j)) for i in range(len(nums) - 3): for j in range(i+1, len(nums) - 2): onesum = nums[i] + nums[j] if target - onesum in sumans: for k in sumans[target - onesum]: if k[0] > j: ans.add((nums[i], nums[j], nums[k[0]], nums[k[1]])) return [i for i in ans]
雖然說我們寫出夾逼的方法,但確實有人寫出了而且比我快,我們主要分析一下他們是怎麼設定過濾條件的。
2.四指標夾逼法
class Solution:
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
if not nums:
return []
_4_sum_list = []
nums.sort()
if nums[-1]*4 < target:
return []
for i in range(len(nums)-3):
if nums[i]*4 > target:
break
if i==0 or nums[i]!= nums[i-1]:
ele = nums[i]
target_3_sum = target - ele
if nums[-1]*3 < target_3_sum:
continue
for j in range(i+1,len(nums)-2):
ele2 = nums[j]
if ele2*3 > target_3_sum:
break
if j==i+1 or ele2!= nums[j-1]:
target_2_sum = target_3_sum - ele2
point_left = j+1
point_right = len(nums)-1
while point_left < point_right:
if nums[point_left] + nums[point_right] > target_2_sum:
point_right -= 1
elif nums[point_left] + nums[point_right] < target_2_sum:
point_left += 1
else:
aaa = [ele, ele2,nums[point_left], nums[point_right]]
_4_sum_list.append(aaa)
point_left += 1
point_right -= 1
while point_left < point_right and nums[point_left] == nums[point_left-1]:
point_left += 1
while point_left < point_right and nums[point_right] == nums[point_right+1]:
point_right -= 1
return _4_sum_list
首先思路很清晰,i,j,point_left,point_right四個指標。
注意 因為經過了排序,i到point_right遞增。
1.如果i也就是第一個指標的四倍大於等於target, break
2.如果最大一項的三倍小於還需要填充的和,則進入下個更大的i。
3.同理它這裡的if保證了不重複計算相同的i.
4。剩下來的思路與三數之和大致相同。具體可參考我的另一篇部落格。https://mp.csdn.net/postedit/80647482
反思總結:1.用字典查詢法。先遍歷求出後幾個值的可能取值,並用字典去儲存,最後去搜索target - nums[i]-nums[j]。
2.夾逼法。定4個指標,後2個指標用來夾逼。最重要的是要會過濾條件,否則它就是暴力法。