0015. 3Sum - chasel2361/leetcode GitHub Wiki

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0?
Find all unique triplets in the array which gives the sum of zero.
The solution set must not contain duplicate triplets.

Given array nums = [-1, 0, 1, 2, -1, -4], A solution set is:
  [[-1, 0, 1],
   [-1, -1, 2]]

最一開始的想法是直接硬幹,把list排序之後,只要湊出三個相加為零的值就挑出來存著

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        n = len(nums)
        ans = []
        for i in range(n):
            for j in range(i+1, n):
                for k in range(j+1, n):
                    num = [nums[i], nums[j], nums[k]]
                    if sum(num) == 0:
                        num.sort()
                        if not num in ans:
                            ans.append(num)
        return ans

這樣的做法雖然可行,但時間複雜度是O(N^3)太高了,不被系統接受,所以開始參考別人的寫法。

首先是medium上大大的解,但無法理解的是,為何認定-v-x一定會出現在給定的list裡

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) < 3:
            return []
        nums.sort()
        res = set()
        for i, v in enumerate(nums[:-2]):
            if i >= 1 and v == nums[i-1]:
                continue
            d = {}
            for x in nums[i+1:]:
                if x not in d:
                    d[-v-x] = 1
                else:
                    res.add((v, -v-x, x))
        return list(res)

因為有無法理解的地方,所以又去研究了其他人的做法,這次的做法是標準的two pointers,

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        res = []
        n = len(nums)        
        for i in range(n-2): # [1]
            if nums[i] > 0: break # [2]
            if i>0 and nums[i] == nums[i-1]: continue # [3]
            
            left = i + 1
            right = n - 1 # [4]
            
            while left < right:
                total = nums[i] + nums[left] + nums[right]
                    
                if total > 0:
                    right -= 1 # [5]
                elif total < 0:
                    left += 1 # [6]
                else:
                    res.append([nums[i], nums[left], nums[right]]) # [7]
                        
                    while left < right and nums[left+1] == nums[left]: # [8]
                        left += 1
                    while left < right and nums[right-1] == nums[right]: # [8]
                        right -= 1                    
                    left += 1
                    right -= 1
        return res

[1] 從左取出i,但因為總共要取三個數,所以i只取至倒數第三個
[2] 因為i是由左至右取出,如果i代表的數字大於零,總和必大於等於零,此時也不必再搜尋了。
[3] 為了避免重複項,當i不為第零項時需檢驗其與下一項代表的數是否相同,若是則跳過當前項。
[4] 剩下兩個數為left及right,left從i+1起由左至右,right從末項起由右至左。
[5] 若總和大於零,需要把right往左移(減少總和)
[6] 若總和小於零則必須將left往右移
[7] 若等於零則表示取到答案。
[8]與i同樣,為了避免重複項,需要檢驗left、right與次項代表的數是否相同,若相同必須往次項移動

這樣寫的時間複雜度是O(N**2),空間複雜度是O(1)

Code Link