1. 程式人生 > >LeetCode15三數之和

LeetCode15三數之和

題目描述:

給定一個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 *a,b,c ,*使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。

**注意:**答案中不可以包含重複的三元組。

例如, 給定陣列 nums = [-1, 0, 1, 2, -1, -4],

滿足要求的三元組集合為:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

解題思路:

這題肯定要寫迴圈遍歷陣列的。

  1. 想到的第一種辦法就是:遍歷陣列,每次都與其後面的陣列進行相加,如果得到的結果的倒數,在剩餘陣列中能夠找到,則成功。

    存在的問題:

    • 如何快速的定位兩數之和的倒數仍在剩餘的陣列中

    • 如果有重複的結果,如何有效的去重

    第一個問題,我採用的辦法是使用python list中的in方法,因為list是使用線性結構儲存的,其查詢複雜度是 O ( n )

    O(n) ,整個程式的時間複雜度就達到了 O ( n 3 )
    O(n^3)
    。列表與字典的儲存結構不同,字典使用Hash表結構來儲存,其存取效率要高很多,時間複雜度是 O ( 1 ) O(1)

    第二個問題,去重也是使用的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
    
  2. 所以想的是,為了提高遍歷效率,要考慮使用雙指標。

    Tips : 一般遍歷問題,都考慮一下是否可以使用雙指標,然後想清楚雙指標的移動條件。

    先對陣列排序,使用雙指標從首尾兩端對陣列進行遍歷。每次都得到首尾兩端數字的和,然後判斷兩數字和的倒數是否在剩餘的列表切片中即可。如果在的話,則加到結果集中。如果不在的話… 額(⊙o⊙)…是個問題。

    如果兩數字之和大於0,則需要右邊(大於0)的指標左移

    如果兩數之和小於0…

    好像這樣講不出道理。。。GG

  3. 雖然上一種做法有問題,不過雙指標的想法還是正確的。所以,應該換個思路。

    先對列表進行排序,然後從頭到尾遍歷。這樣每次都固定住一個值,而不是用雙指標來解決三值問題,好像是可行的。

    在固定一個值之後,剩下的有序列表中,用雙指標法尋找兩個值的和是否是固定值的負數。這樣指標移動的標準也就出來了。如下圖:

    • 當兩值之和大於固定值的負數時,右邊的指標左移
    • 當兩值之和小於固定值的負數時,左邊指標右移
    • 當兩值之和恰好為固定值負數,儲存結果。此時有個很有必要的操作:判斷 start+1的值,end-1的值,是否與上一個一樣,相同的話,則跳過處理,我一開始就沒處理,想偷懶最後儲存結果的時候去重即可,但是去重要使用in方法, O ( n ) O(n) 的複雜度,增加了時間複雜性。

解答:

實現是使用的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