【回溯】回溯法解决组合问题

本文介绍了如何使用回溯算法解决寻找整数数组中特定数量的数的组合问题。分别展示了三种情况:1) 给定两个整数n和k,返回[1,n]中的所有k个数的组合;2) 找出数组中所有和为目标数的组合,数组中的数可以重复选取;3) 在有重复数字的数组中找到所有和为目标数的组合,但每个数字只能使用一次。每种情况都提供了详细的代码实现,并对比了不同剪枝策略的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        path = []
        res = []
        startindex = 1
        def backtracking(n, k, startindex):
            if len(path) == k:
                res.append(path[:])
                return 
            for i in range(startindex, n + 2 - (k -len(path))):  # 剪枝
                path.append(i)
                backtracking(n, k, i + 1)
                path.pop()
            
        backtracking(n, k, startindex)
        return res
  • 题目2:给你一个 无重复元素 的整数数组 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 。
    仅有这两种组合
    题目链接:https://leetcode-cn.com/problems/combination-sum/
# 写法一:
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        path = []
        def backtracking(candidates, target, startindex, sum_):
            if sum_ == target:
                res.append(path[:])
                return
            for i in range(startindex, len(candidates)):
            # 这样写原数组candidates可以不用排序,因为它会在本层遍历所有的数
            # 如果有符合条件的就继续,如果都不符合就回溯到上一层遍历
                if sum_ + candidates[i] <= target:  
                    sum_ += candidates[i]
                    path.append(candidates[i])
                    # 可以重复使用,所以到下一层时还是可以从下标为i的地方开始取数
                    backtracking(candidates, target, i, sum_)  
                    sum_ -= candidates[i]
                    path.pop()

        backtracking(candidates, target, 0, 0)
        return res
# 写法二:
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []
        path = []
        def backtracking(candidates, target, startindex, sum_):
            if sum_ == target:
                res.append(path[:])
                return
            for i in range(startindex, len(candidates)):
            # 这样写原数组必须要从小到大排序,当 sum_ + 下一个数大于target时,
            # 说明后面的所有数 + sum_都大于target,这时就可以停止本层的遍历,直接回溯到上一层遍历
                if sum_ + candidates[i] > target: return 
                sum_ += candidates[i]
                path.append(candidates[i])
                # 可以重复使用,所以到下一层时还是可以从下标为i的地方开始取数
                backtracking(candidates, target, i, sum_)
                sum_ -= candidates[i]
                path.pop()
                
        candidates.sort()  # 必须对数组排序
        backtracking(candidates, target, 0, 0)
        return res
"""
这两种写法都进行了剪枝,但是具体写法不同
方法二: 对原数组从小到大排序,当 sum_ + 下一个数大于target时,
        说明后面的所有数 + sum_都大于target,这时就可以停止本层的遍历,直接回溯到上一层遍历
方法一:若是返回上一层,必须对本层数据都进行判断,如果途中有符合条件的数,则会进入下一层,若是都不 
		符合才会返回上一层,
		假设我们已知本层数据都不符合条件,但对于计算机来说并不知道所以它必须都判断一次
		然后才能返回上一层
对比:方法二是花费一个排序的时间,然后在后续的每层遍历中赢得时间
	  方法一是省了一个排序的时间,但是在后续的每层遍历中花费了时间
	  总的来说写法二更优
	  方法一我写的,方法二别人写的!(T-T)
"""
class Solution:
    def combinationSum2(self, candidates, target):
        path = []
        res = []
        def backtracking(candidates, target, startindex, sum_):
            if sum_ == target:
                res.append(path[:])
                return
            for i in range(startindex, len(candidates)):
                if sum_ + candidates[i] > target: return
                # 判断在本层是否使用过 
                if i > startindex and candidates[i] == candidates[i-1]: continue
                sum_ += candidates[i]
                path.append(candidates[i])
                # 不可以重复使用,所以到下一层时i不能取要从下标为i+1的地方开始取数
                backtracking(candidates, target, i + 1, sum_)
                sum_ -= candidates[i]
                path.pop()
        
        candidates.sort()  # 这里必须排序
        backtracking(candidates, target, 0, 0)
        return res
"""
这道题与上一道的区别在于:
	上一道数组中数不重复,而且可以重复取用
	这道数组中的数有重复,而且不可以重复取用


这道题先进行排序,这样一样的数据就会挨在一起,然后每层遍历时,判断在本层是否使用过该数字
						例如 1 1 1 2 2 2 3 3 3
			第一层取 1     ↙   ↘ 但是回溯到第一层遍历时不能再取1,只能从2开始 
				剩余 1 1 2 2 2 3 3 3 
第二层还可以取 1    ↙  ↘但是回溯到第二层遍历时不能再取1,因为本层已经取过只能从2开始
			剩余 1 2 2 2 3 3 3
   还可以取1   ↙取2↘ ↘ 第2次回溯到本层遍历后续数字时,不能再取2,
		    ↙本层第一次取2                 因为第1次回溯到本层已经用过一次
      略  ↙
"""

这个题目描述不好理解,大家可以参考
官方和各路神仙的题解:https://leetcode-cn.com/problems/combination-sum-ii/solution/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值