组合问题
77.组合
在写题目前,我们先回顾一下我们的回溯算法的答题模板。
res = []
def backtrack(未探索区域, res, path):
if 满足条件:
res.append(path)
# return # 如果不用继续搜索需要 return
for 选择 in 未探索区域当前可能的选择:
if 当前选择符合要求:
backtrack(未探索区域, res, path+已探索的区域)
题目链接:77. 组合 - 力扣(LeetCode) (leetcode-cn.com)
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。你可以按 任何顺序 返回答案。
输入:n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
这题算是一道很经典的回溯算法的题目,很多一看这道题就会想到用for语句暴力循环,但是随着n和k的增大,算法就很容易超时,不太理想,所以我们可以想到用回溯法来解决问题。
我们上篇说到,回溯法可以把问题抽象弄成树形结构,更好地理解:
我们把每次的数字保存在path里,当path的数组大小达到了k,就是找到一组结果集合。
回溯法有三部曲:
- 递归函数的返回值以及参数
- 回溯函数终止条件
- 单层搜索的过程
组合问题Python3完整代码如下:
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
nums = [i for i in range(1,n+1)]//创建从1到n的数组
ans = []//存放最后答案的数组
def backtrack(nums,path):
if len(path) == k:// 当满足k长度的我们存进数组
ans.append(path)
return
for i in range(len(nums)):
backtrack(nums[i+1:],path + [nums[i]])//递归
backtrack(nums,[])
return ans
对比一下本题的代码,是不是跟我们模板很像,所以有了这个模板,就有了解题的大体方向。
77.组合
题目链接:39. 组合总和 - 力扣(LeetCode) (leetcode-cn.com)
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
示例1:
输入:candidates = [2,3,6,7],target = 7
输出:[[2,2,3],[7]]
示例2:
输入: candidates = [2,3,5],target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
本题搜索的过程抽象成树形结构如下:
本题特别的是他没有数组数量的要求,即递归是没有层数限制,只要选取的元素综合超过target就返回。
这题我们需要剪枝优化,当sum>target时候,就可以结束本轮for循环的遍历。
整体代码如下:
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()//先对无序数组进行排序
ans = []
n = len(candidates)
def backtrack(candidates,target,res):
if target == 0:
ans.append(res)
if target < 0://数组和已经超target就没必要递归
return
for i,c in enumerate(candidates):
backtrack(candidates[i:],target-c,res+[c])
backtrack(candidates,target,[])
return ans
总结:
两道题都是组合的题目,但是每道题的限制条件不一样,元素重不重复决定下次递归的时候数组的起始位置不同,相同的都是其答题模板都相似。
在求和问题中,排序之后加剪枝是常见的套路。