回溯法总结
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
说明:
```
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
```
示例 1:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[[7],[2,2,3]]
思路:标准的回溯法的解法,加上了剪枝而且对于重复的元素进行了去重
class Solution {
List<List<Integer>> result = new ArrayList<>();
ArrayList<Integer> path =new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
backtrack(candidates,target,0,this.path);
return this.result;
}
public void backtrack(int[] candiates,int target,int start,ArrayList<Integer> path){
//递归的结束条件
if (target==0){
this.result.add(new ArrayList(path));}
else{
for(int i=start;i<candiates.length && target-candiates[i]>=0;i++){
this.path.add(candiates[i]);//循环里面实际上加了剪枝
backtrack(candiates,target-candiates[i],i,path);//注意这里为i而不是i加1 因为元素是可以复用的
this.path.remove(path.size()-1);
}
}
}
}
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
思路:跟前一道题是一模一样的,只不过元素不可以复用。代码里面存在两个剪枝
class Solution {
List<List<Integer>> result = new ArrayList<>();
ArrayList<Integer> path =new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backtrack(candidates,target,0,this.path);
return this.result;
}
public void backtrack(int[] candiates,int target,int start,ArrayList<Integer> path){
//递归的结束条件
if (target==0){
this.result.add(new ArrayList(path));}
else{
for(int i=start;i<candiates.length && target-candiates[i]>=0;i++){
//治理要处理一下重复的问题
if(i>start&&candiates[i]==candiates[i-1]){//这里其实也是进行了剪枝
continue;
}
this.path.add(candiates[i]);//循环里面实际上加了剪枝
backtrack(candiates,target-candiates[i],i+1,path);//注意这里为i+1因为这里的元素是不可复用的
this.path.remove(path.size()-1);
}
}
}
}
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
class Solution {
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
ArrayList<Integer> path =new ArrayList<>();
backtrack(k,n,1,path);
return result;
}
public void backtrack(int k, int n,int start,ArrayList<Integer> path){
if (path.size()==k && n==0){
result.add(new ArrayList(path));//对于引用类型我们需要新建一个地址给他不可以直接将地址给他否则最后答案都是空
}else if (path.size()==k || n==0){//实际上这里是剪枝操作
return;
}else{
for(int i=start;i<=9 && n-i>=0;i++){
path.add(i);
backtrack(k,n-i,i+1,path);
path.remove(path.size()-1); }
}
}
}
回溯法超过了时间限制因为回溯法的时间复杂度其实是树状的。
既然他不是求出所有的情况而是让你求出有多少钟,因此我们可以考虑使用动态规划的方法来做这道题。
class Solution:
def __init__(self):
self.result=0
def combinationSum4(self, nums: List[int], target: int) -> int:
path=[]
size=len(nums)
self.backtrack(nums,target,0,size,path)
return self.result
def backtrack(self,nums,target,start,size,path):
if target==0:
self.result+=1
elif target<0:
return
else:
for i in range(start,size):
path.append(nums[i])
self.backtrack(nums,target-nums[i],start,size,path)
path.pop()
本文总结了回溯法的基本概念,并通过LeetCode的39、40、216、377题进行实战解析,展示了如何结合剪枝优化算法性能。在面对组合总和问题时,注意剪枝策略和重复元素处理,对于超时问题,提出了动态规划的替代解决方案。
1万+

被折叠的 条评论
为什么被折叠?



