递归&回溯&剪枝-子集

LCR 079. 子集 - 力扣(LeetCode)

方法一 

1. 决策树:对于决策树,思考的角度不同,画出的决策树也会不同,这道题可以从两个角度来画决策树。

2. 考虑全局变量的使用:

使用全局变量 List<List<Integer>> ret 来存子集;

使用全局变量 List<Integer> path 来存递归过程中的值;

3. 关注递归本身,回溯,剪枝,递归出口:

1. 递归本身:使用方法 dfs(nums,i),nums为参数数组,i 表示当前进行选择或者不选择的目标数是 nums[i],当选择目标数的时候,path + nums[i] 然后递归下一轮,不选择的时候,直接递归下一轮,dfs(nums,i+1);

2. 剪枝:从决策树可以看出,这道题是不需要到剪枝环节的;

3. 回溯:当决策树中的节点对目标数进行判断完成后,需要进行 "恢复现场" 操作,也就是需要将当前的全局变量 path 的最后一个元素去掉,从而恢复现场,可以按下图来理解;

4. 递归出口:当 dfs(nums,i) 中 i 的值 == nums.size 的时候,说明已经超出数组的范围了,此时就可以返回了;

代码实现 

class Solution {
    List<List<Integer>> ret;
    List<Integer> path;
    public List<List<Integer>> subsets(int[] nums) {
        ret = new ArrayList<>();
        path = new ArrayList<>();
        dfs(nums,0);
        return ret;
    }
    public void dfs(int[] nums,int i){
        // 递归出口
        if(i == nums.length){
            ret.add(new ArrayList(path));
            return;
        }
        // 选
        path.add(nums[i]);
        dfs(nums,i+1);
        // 回溯,恢复现场
        path.remove(path.size()-1);
        // 不选
        dfs(nums,i+1);

    }
}

方法二 

 第二种决策树:这种思考方式,就是从选择多少个元素来考虑,但要求的是从数组 i 定位从小到大进行选择,在选择完前 n 个元素后,继续选择 n+1 个元素时,只能是选择当前 i 之后对应的元素,也就是数组 [1,2,3] 当选择到 2 的时候,再进行选择时,就只能选 3 了,不能选 1 ,这样是为了避免重复情况出现;

2. 全局变量的使用与第一种方法一样; 

3. 关注递归本身,回溯,剪枝,递归出口:

1. 观察决策树,可以发现每一个节点都作为子集,也就是每次进入都可以作为一个结果然后存进全局变量 ret 中;

2. dfs(nums[],i) 此处的 i 可以理解为当前的 path 要从 i 开始进行选择;

3. 跟第一种情况相同,不需要进行剪枝;

4. 回溯也跟第一种情况相同,将最后一个元素去掉;

5. 并且要注意,在这种情况下,是没有递归出口的,因为每个节点都作为子集,在 for 循环中循环结束后就会自动返回;

代码实现 

class Solution {
    List<List<Integer>> ret;
    List<Integer> path;
    public List<List<Integer>> subsets(int[] nums) {
        ret = new ArrayList<>();
        path = new ArrayList<>();
        dfs(nums,0);
        return ret;
    }
    public void dfs(int[] nums,int i){
        // 每个节点都是子集,进入就添加到 ret 中
        ret.add(new ArrayList(path));

        for(int j=i;j<nums.length;j++){     // 从节点 i 开始
            path.add(nums[j]);
            dfs(nums,j+1);      
            // 回溯,恢复现场
            path.remove(path.size()-1);
        }
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
回溯法是一种通过枚举所有可能的解来求解问题的算法,其核心思想是“试错”,即在解空间中搜索可能的解,每到一个节点时,先尝试一种可能的选择,如果发现这种选择不能得到正确的解,就回溯到上一个节点,然后再尝试另一种可能的选择,直到找到正确的解或者所有的选择都尝试完毕。 回溯法的求解步骤如下: 1. 定义问题的解空间:确定问题的解空间,即问题的所有可能解的集合。 2. 确定搜索的过程:在解空间中搜索可能的解,搜索的过程通常是一个深度优先搜索过程。 3. 确定解的组成方式:确定问题的解的组成方式,即确定一个解是由哪些部分组成的。 4. 判断是否满足约束条件:在搜索的过程中,需要判断每个解是否满足约束条件,如果不满足约束条件,则回溯到上一个节点。 5. 判断是否是最优解:在搜索的过程中,需要判断每个解是否是最优解,如果不是,则回溯到上一个节点。 6. 输出结果:如果找到一个正确的解,就输出结果。 回溯法的剪枝函数类型通常有以下几种: 1. 可行性剪枝:在搜索的过程中,如果发现当前的解已经不能满足约束条件,就直接回溯到上一个节点。 2. 最优性剪枝:在搜索的过程中,如果发现当前的解已经不能成为最优解,就直接回溯到上一个节点。 3. 约束传播:在搜索的过程中,根据已经确定的变量的取值,推导出其他变量的取值范围,从而缩小搜索的空间。 子集树是回溯法中常用的一种搜索策略,其搜索的过程就是在一个集合中选择一些元素,使得这些元素构成的集合满足某些条件。 回溯法有两种实现方式:递归回溯和迭代回溯递归回溯是指使用函数的递归调用来实现回溯过程,而迭代回溯是指使用循环来实现回溯过程。这两种实现方式的框架如下: 递归回溯框架: ```python def backtrack(路径, 选择列表): if 满足结束条件: 结果.append(路径) return for 选择 in 选择列表: 做出选择 backtrack(路径, 选择列表) 撤销选择 ``` 迭代回溯框架: ```python def backtrack(选择列表): stack = [(路径, 选择列表)] while stack: 路径, 选择列表 = stack.pop() if 满足结束条件: 结果.append(路径) for 选择 in 选择列表: 做出选择 stack.append((路径, 选择列表)) 撤销选择 ``` 以上就是回溯法求解思路、求解步骤、剪枝函数类型、子集树的类型以及递归回溯和迭代回溯框架的介绍。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PlLI-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值