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]]
相关文章
相关标签/搜索