1. 程式人生 > >Leetcode演算法——18、四數之和

Leetcode演算法——18、四數之和

題目

給定一個長度為n的整數陣列,一個目標整數target。

在陣列中找到4個數a,b,c,d,使得 a+b+c+d=target。

要求找到所有不重複的四元組合。

示例:

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

思路

1、三數之和+外層迴圈

在三數之和的雙指標法的基礎上,最外層再多巢狀一層迴圈即可。

四數之和的整體架構為:

  • 1、外面雙層迴圈,代表四個數中的前兩個數。
  • 2、裡面為一首一尾雙指標,代表四個數中的後兩個數,雙指標逐步往中間移動,直至相遇。

2、改進版

改進的關鍵主要體現在迴圈之前先通過最小几個數相加和最大幾個數相加判斷是否有繼續巢狀迴圈的必要。如果最小几個數相加都大於目標數,或者最大幾個數相加都小於目標數,則可以直接跳過。

另外,可以將迴圈改為遞迴。使用遞迴是為了防止程式碼重複,否則第一層和第二層迴圈都要加上提前判斷的程式碼。

使用遞迴的另一個好處是,可以不僅僅是4個數相加,任意大於等於3的數相加,都可以用這套程式碼。

python實現

def fourSum(nums, target):
    """
    :type nums: List[int]
    :type target: int
    :rtype: List[List[int]]
    在3Sum的基礎上,多一層迴圈。
    """
if len(nums) < 4: return [] nums.sort() res = [] for o in range(len(nums) - 3): if o > 0 and nums[o-1] == nums[o]: # 避免o重複 continue for i in range(o + 1, len(nums) - 2): if i > o+1 and nums[i-1] == nums[i]: # 避免i重複 continue
l, r = i + 1, len(nums) - 1 while l < r: s = nums[o] + nums[i] + nums[l] + nums[r] if s == target: res.append([nums[o], nums[i], nums[l], nums[r]]) l += 1 r -= 1 while l < r and nums[l] == nums[l-1]: # 避免l重複 l += 1 while l < r and nums[r] == nums[r+1]: # 避免r重複 r -= 1 elif s < target: l += 1 else: r -= 1 return res def fourSum2(nums, target): """ :type nums: List[int] :type target: int :rtype: List[List[int]] 上述方法的改良版。 """ def findComb(nums, size, target, ans, start, ansSize, tempAns): ''' 遞迴函式。從nums的start下標開始往後,找到ansSize個整數,使得相加等於target。 size為nums的大小,固定不變。 ans存放最終答案。 tempAns存放當前4元組內已經找到的元素,len(tmpAns)總是等於4-ansSize。 ''' if sum(nums[start:start+ansSize]) > target: # 最小的幾個數相加都超出 return if sum(nums[-ansSize:]) < target: # 最大的幾個數相加都不夠 return if ansSize == 2: # 需要尋找2個,則使用首尾指標法。 i = start j = size - 1 while(i < j): sigma = nums[i] + nums[j] if sigma > target: j -= 1 elif sigma < target: i += 1 else: ans.append(tempAns + [nums[i], nums[j]]) j -= 1 while(i < j and nums[j] == nums[j + 1]): # skip repeats j -= 1 i += 1 while(i < j and nums[i] == nums[i - 1]): # skip repeats i += 1 else: for i in range(start, size - ansSize + 1): if i > start and nums[i] == nums[i - 1]: # skip repeats continue findComb(nums, size, target - nums[i], ans, i + 1, ansSize - 1, tempAns + [nums[i]]) ans = [] size = len(nums) if size < 4: return [] if size == 4: if sum(nums) == target: return [nums] else: return [] nums.sort() findComb(nums, size, target, ans, 0, 4, []) return ans if '__main__' == __name__: nums = [0, 0, 0, 0] target = 0 print(fourSum2(nums, target))