Leetcode 167:兩數之和 II - 輸入有序陣列(最詳細解決方案!!!)

coordinate_blog發表於2018-05-30

給定一個已按照升序排列 的有序陣列,找到兩個數使得它們相加之和等於目標數。

函式應該返回這兩個下標值 index1 和 index2,其中 index1 必須小於 index2。

說明:

  • 返回的下標值(index1 和 index2)不是從零開始的。
  • 你可以假設每個輸入只對應唯一的答案,而且你不可以重複使用相同的元素。

示例:

輸入: numbers = [2, 7, 11, 15], target = 9
輸出: [1,2]
解釋: 2 與 7 之和等於目標數 9 。因此 index1 = 1, index2 = 2 。

解題思路

首先想到的解決思路是通過兩次迴圈,每次迴圈遍歷列表,這種演算法的時間複雜度是O(n^2)。(這是我們在Leetcode 1 兩數之和中提到的解決方案)

class Solution:
    def twoSum(self, numbers, target):
        """
        :type numbers: List[int]
        :type target: int
        :rtype: List[int]
        """
        nums_len = len(numbers)
        for i in range(nums_len):
            for j in range(i + 1, nums_len):
                if numbers[i] + numbers[j] == target:
                    return [i + 1, j + 1]

        return []

但是很明顯,這種暴力解法在這問題中顯得很蠢(⊙﹏⊙)

我們知道這個資料是有序的,那麼我們很容易聯想到使用二分搜尋法是不是可以用於這個問題呢?我們可以每次判斷target-num[i]對應的值是否在num[i+1:]中,這個時候演算法的複雜度變成了O(nlogn)

class Solution:
    def _BinarySearch(self, numbers, target):
        l = 0
        r = len(numbers) - 1       
        while l <= r:
            mid = (l + r) // 2
            if target == numbers[mid]:
                return mid
            elif target < numbers[mid]:
                r = mid - 1
            else:
                l = mid + 1
        return -1

    def twoSum(self, numbers, target):
        """
        :type numbers: List[int]
        :type target: int
        :rtype: List[int]
        """
        nums_len = len(numbers)
        for i in range(nums_len):
            dif = target - numbers[i] 
            dif_index = self._BinarySearch(numbers[i + 1:], dif)
            if dif_index != -1:
                return [i + 1, dif_index + i + 2]
        return []

那麼我們就想有沒有O(n)級別的做法呢?我們依然可以參考之前Leetcode 1 兩數之和中使用hash表的方法,但是這種做法的空間複雜度就變成了O(n)。我們有沒有空間複雜度O(1),時間複雜度O(n)的做法呢?我們要充分利用題目給的條件:有序陣列

0  1  2  3  4  5  6  7
l->                <-r

我們可以這樣想,我們首先判斷首尾兩項的和是不是target,如果比target小,那麼我們左邊+1位置的數(比左邊位置的數大)再和右相相加,繼續判斷。如果比target大,那麼我們右邊-1位置的數(比右邊位置的數小)再和左相相加,繼續判斷。我們通過這樣不斷放縮的過程,就可以在O(n)的時間複雜度內找到對應的座標位置。(這和快速排序的思路很相似)

class Solution:
    def twoSum(self, numbers, target):
        """
        :type numbers: List[int]
        :type target: int
        :rtype: List[int]
        """
        l = 0
        r = len(numbers) - 1
        while l < r:
            if numbers[l] + numbers[r] == target:
                return [l + 1, r + 1]
            elif numbers[l] + numbers[r] < target:
                l += 1
            else:
                r -= 1
        return []

這種解法叫做對撞指標法,又叫做雙索引法

該問題的其他語言版本新增到了我的GitHub Leetcode

如有問題,希望大家指出!!!

相關文章