zoukankan      html  css  js  c++  java
  • 39. 组合总和

    39. 组合总和

    题意

    给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

    candidates 中的数字可以无限制重复被选取。

    说明:

    • 所有数字(包括 target)都是正整数。

    • 解集不能包含重复的组合。

    解题思路

    由于最终的结果要求是有序的,因此需要先将数组进行排序;

    1. 回溯:维持一个路径组合,并且不断的将target减去数组中的值,直到target值为0,则把路径组合加入到最终的结果中;

    1. 回溯:思路和上面的一样(思路和1相似,只不过在原来函数的基础上操作);

    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]]

  • 相关阅读:
    装饰者模式和适配器模式
    java 中获得 资源文件方法
    在windows 上统计git 代码量
    Linux-静态库生成
    Redis-Redi事务注意事项
    Linux-使用 yum 升级 gcc 到 4.8
    Linux-配置虚拟IP
    PHP-PSR-[0-4]代码规范
    Linux-/etc/rc.local 或 service 中使用 sudo -u xxx cmd 执行失败(sorry, you must have a tty to run sudo)解决办法
    Linux-系统负载
  • 原文地址:https://www.cnblogs.com/George1994/p/10657459.html
Copyright © 2011-2022 走看看