39. 组合总和

122 阅读4分钟

【题目】

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入: candidates = [2,3,6,7], target = 7
输出: [[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

【题目解析】

思路

要解决这个问题,我们采用回溯算法,这是一种通过探索所有可能的候选解来寻找所有解的算法。在回溯过程中,我们构造候选解,并一步步地尝试解决问题。如果当前的候选解最终不满足条件,我们会回溯到上一个步骤,然后尝试另一种可能的候选解。

具体步骤如下:

  1. 选择路径:从候选数组的开始选择一个起点。
  2. 构建解决方案:逐步添加候选数字至当前组合,如果当前组合的和等于目标数,则将其加入结果列表。
  3. 回溯:如果当前组合的和大于目标数,或者我们已经到达候选数组的末尾,我们回溯到上一个决策点。
  4. 重复尝试:从最后一个决策点尝试不同的候选数字,直到所有可能的组合都被探索完毕。
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(remain, combo, start):
            if remain == 0:
                result.append(list(combo))
                return
            elif remain < 0:
                return
            
            for i in range(start, len(candidates)):
                combo.append(candidates[i])
                backtrack(remain - candidates[i], combo, i)
                combo.pop()
        
        result = []
        backtrack(target, [], 0)
        return result

执行

image.png

【总结】

组合总和问题是一个经典的搜索问题,它要求我们找出所有可能的组合,这些组合的元素和等于一个给定的目标数。这个问题涉及到一个关键的概念:如何从一组候选数中选择若干个数,使得它们的和等于目标数。

适合的问题类型

这类方法适合用于以下类型的问题:

  • 组合搜索问题:当需要从一组数中找出满足特定条件的所有组合时。
  • 决策树问题:在每一步都有多个选择,需要探索所有可能性以找到解决方案。
  • 约束满足问题:需要满足一系列约束条件,找到所有可行解。
  • 无限重复选择问题:与组合总和问题类似,候选数可以被无限次选择。

使用的算法

解决组合总和问题的算法是回溯算法。回溯算法是一种通过探索所有可能路径来找到所有解的算法。它类似于一种有系统的试错法,通过从一系列的决策点开始,逐步前进来构建解决方案,如果当前路径不再满足条件,就回溯到上一步或几步,尝试其他可能的选项。

解题策略

具体到组合总和问题,解题策略涉及以下步骤:

  1. 追踪当前组合:使用一个列表来保存当前的组合状态。
  2. 递归和回溯:递归地选择下一个可能的候选数,如果不满足条件就回溯。
  3. 处理候选数重复选择:由于同一个数可以重复选择,我们在递归时保持候选数的索引不变。
  4. 剪枝:如果当前组合的和已经大于目标数,提前终止这条路径的探索。

总结

回溯算法在解决这类问题时非常有效,因为它能够确保所有可能的解都被探索到,而不遗漏任何一个可能的组合。同时,算法的剪枝操作还可以减少不必要的计算,提高解题效率。

在实际应用中,回溯算法是解决组合问题的强大工具。不仅在算法竞赛中,在实际的软件工程中,如数据库查询优化、AI游戏设计、解决约束满足问题等领域,回溯算法都有着广泛的应用。

对于希望提升算法能力的开发者而言,掌握回溯算法以及相关的剪枝技巧是非常有价值的。这不仅能帮助解决类似的算法题,更能在面对实际问题时,设计出高效且可靠的算法解决方案。

题目链接

组合总和