回溯法总结附带leetcode例题

本文总结了回溯法的基本概念,并通过LeetCode的39、40、216、377题进行实战解析,展示了如何结合剪枝优化算法性能。在面对组合总和问题时,注意剪枝策略和重复元素处理,对于超时问题,提出了动态规划的替代解决方案。

回溯法总结


39. 组合总和

给定一个无重复元素的数组 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);    

            }

        }

    }
}

40. 组合总和 II

给定一个数组 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);    

           }

        }

    }
}

216. 组合总和 III

找出所有相加之和为 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);      }
        }

    }
}

377. 组合总和 Ⅳ

回溯法超过了时间限制因为回溯法的时间复杂度其实是树状的。

既然他不是求出所有的情况而是让你求出有多少钟,因此我们可以考虑使用动态规划的方法来做这道题。

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()
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值