1. 程式人生 > >Leetcode 018 四數之和 思路詳解+反思易錯 Python實現

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個指標用來夾逼。最重要的是要會過濾條件,否則它就是暴力法。