39. 組合總和

39. 組合總和

題意

給定一個無重複元素的數組 candidates 和一個目標數 target ,找出 candidates 中全部可使數字和爲 target 的組合。web

candidates 中的數字能夠無限制重複被選取。數組

說明:app

  • 全部數字(包括 target)都是正整數。ide

  • 解集不能包含重複的組合。 函數

解題思路

因爲最終的結果要求是有序的,所以須要先將數組進行排序;spa

  1. 回溯:維持一個路徑組合,而且不斷的將target減去數組中的值,直到target值爲0,則把路徑組合加入到最終的結果中;code

  1. 回溯:思路和上面的同樣(思路和1類似,只不過在原來函數的基礎上操做);orm

  1. 記憶化搜索:經過字典記錄下每一個和對應的組合,在target在不斷減去數組中的值的時候,若是這個和已經出現過,那麼直接返回該和對應的組合;對象

  1. 動態規劃:維護一個的記錄下從1到target每一個值對應的組合的三維數組,一樣的,在target在不斷減去數組中的值的時候,若是這個已經出現過,則能夠經過下標找到對應的組合便可(思路和3類似,一樣是維護每一個和對應的組合);排序

實現

class Solution(object):
   def combinationSum(self, candidates, target):
       """
      :type candidates: List[int]
      :type target: int
      :rtype: List[List[int]]
      """
       res = []
       nums_len = len(candidates)
       candidates.sort()

       def helper(index, target, path):
           # 邊界條件爲剩餘關鍵字減到了0,代表path中的值的和已經知足條件
           if not target:
               res.append(path)
               return
           for idx in range(index, nums_len):
               # 若是剩餘關鍵字比當前數字還要小的話,後面就沒有循環的必要了
               # 因此從idx後面的繼續找;
               if target >= candidates[idx]:
                   helper(idx, target - candidates[idx], path + [candidates[idx]])
               else:
                   break
       
       helper(0, target, [])
       return res

   def combinationSum(self, candidates, target):
       """
      :type candidates: List[int]
      :type target: int
      :rtype: List[List[int]]
      """
       res = []
       candidates.sort()

       for idx, num in enumerate(candidates):
           if num > target:
               break
           if num == target:
               res.append([num])
               break
# 從idx後面遞歸出目標值爲`target - num`的數組,因爲數組是排好序的
           # 所以往這些數組中加入num到第一個位置
           back_res = self.combinationSum(candidates[idx:], target - num)
           for back in back_res:
               back.insert(0, num)
               res.append(back)
           
       return res
     
def combinationSum(self, candidates, target):
       """
      :type candidates: List[int]
      :type target: int
      :rtype: List[List[int]]
      """
       # 記錄每一個和對應的組合
       memorize = {}
       candidates.sort()

       def helper(target):
           if target in memorize:
               return memorize[target]
           
           new_conbinations = []
           for num in candidates:
               if num > target:
                   break
               elif num == target:
                   new_conbinations.append([num])
               else:
                   old_conbinations = helper(target-num)
                   for conbination in old_conbinations:
                       if num > conbination[0]:
                           continue
                       # 因爲不可以確保num是不是正確的,可以加入到結果數組中,而且數組是可變對象
                       # 所以不可以將num append 到數組中
                       # 加入到前面是由於能夠保證順序,這是由於循環是從小到大的順序來找出對應的target
                       new_conbinations.append([num] + conbination)
           
           memorize[target] = new_conbinations
           return new_conbinations
       
       helper(target)
       return memorize[target]
     
def combinationSum(self, candidates, target):
       """
      :type candidates: List[int]
      :type target: int
      :rtype: List[List[int]]
      """
       # 三維數組,記錄每一個和對應的最終結果
       dp = []
       candidates.sort()
# cur表明這個下標,也就是這個和,因此從1開始
       for cur in range(1, target+1):
           conbinations = []
           for num in candidates:
               if num > cur:
                   break
               elif num == cur:
                   conbinations.append([cur])
                   break
               else:
                # 減去1是由於下標的關係
                   for conbination in dp[cur-num-1]:
                       if num > conbination[0]:
                           continue
                       conbinations.append([num] + conbination)
           dp.append(conbinations)
       return dp[target-1]

拓展

由於上面求出來的結果中,每一個子數組的排序是亂序的,若是想要最終的順序按照從短到長進行排序,應該怎麼辦呢?

增長當前深度和最大的深度,由於子數組的長度表明着遞歸的深度,所以只要將遞歸的層數從小到大進行排序,那麼就能夠作到最終的結果按照子數組的長度從小到大進行排序了。

class Solution(object):
   def combinationSum(self, candidates, target):
       """
      :type candidates: List[int]
      :type target: int
      :rtype: List[List[int]]
      """
       res = []
       nums_len = len(candidates)
       candidates.sort()

       def helper(index, cur_depth, max_depth, target, path):
           # 邊界條件爲到達規定的深度
           if cur_depth == max_depth:
               if not target:
                   res.append(path)
               return
           for idx in range(index, nums_len):
               # 若是剩餘關鍵字比當前數字還要小的話,後面就沒有循環的必要了
               # 因此從idx後面的繼續找;
               if target >= candidates[idx]:
                   helper(idx, cur_depth + 1, max_depth, target - candidates[idx], path + [candidates[idx]])
               else:
                   break

       # target // candidates[0] 是爲了統計最大的深度,由於candidates[0]是最小值
       # 所以頂多會有target // candidates[0]個數字進行組合到一塊兒
       for depth in range(target // candidates[0] + 1):
           helper(0, 0, depth, target, [])
       return res

res = Solution().combinationSum([2,3,6,7], 7)
print res  # [[7], [2, 2, 3]]
相關文章
相關標籤/搜索