当前位置: 首页 > news >正文

【递归、搜索和回溯算法】专题三 :穷举VS暴搜VS深搜VS回溯VS剪枝

回溯算法

回溯算法是一种经典的递归算法,通常用于解决组合问题、排列问题和搜索问题等。

基本思想:从一个初始状态开始,按照一定的规则向前搜索,当搜索到某个状态无法前进时,回退到钱一个状态,再按照其他的规则搜索。

核心思想:“试错”,在搜索中不断地做出选择,如果选择正确,继续向前搜索,否则回退到上一个状态,重新作出选择。

1.全排列

46. 全排列 - 力扣(LeetCode)

首先我们先来了解这题的思路,它时一个排列问题就可以用上回溯地思想去解决,再结合之前经验,我们可以先全局变量来记录并保存数据。既然返回的是一个数组包含数组地问题,我们就用全局变量,一个用来记录当前的数组排列path,如果满足数组nums中地数据那就保存到另一个全局变量ret中,还有一个全局变量check负责剪枝, 如果出现重复地数据那就跳过,然后通过回溯返回上一个下标。

大致的流程就是以上所说地,现在来仔细叙述:

先创建三个全局变量ret、path、check,再将它们初始化,然后调用递归方法最后返回ret;

    public static List<List<Integer>> ret;
    public static List<Integer> path;
    public static boolean[]check;
    public static List<List<Integer>> permute(int[] nums) {
        ret = new ArrayList();
        path = new ArrayList();
        check = new boolean[nums.length];
        dfs(nums);
        return ret;
    }

现在主要写递归方法,如果path的大小已经满足数组nums的大小,那直接将path添加到ret中,再返回;

    if (path.size() == nums.length){
            ret.add(new ArrayList<>(path));
            return;
    }

通过循环来确定数组中的第一个数据,这样每一次排序都不会落下,确定完第一个那接着就是第二个,由于我们设置的全局变量check是用来记录已经存在的数据的,最开始创建check布尔类型它默认全为false,所以如果i对应的check是false说明这个位置下的数组是没有被存放进path的,所以我们就需要将i对应的下标数据存放进path中,然后再将该位置check标记为已记录(即为true),然后递归该数组直到path中已经存放的长度等于原先数组长度就返回。最后我们还需要返回现场,最后这个返回现场很重要,如果i还是小于nums.length,那就需要进行的同一层的另一个下标数据,在比较之前就需要将该位置的check置为false,且删除path中最后一个的数据方便下一个下标进行插入。

2.子集

 78. 子集 - 力扣(LeetCode)

解法一:我们可以使用上一题全排列的解法通过选和不选数组中的元素来进行解答。首先还是需要全局变量一个用来存储ret,一个用来记录path。

    public List<List<Integer>> subsets(int[] nums) {
        ret = new ArrayList<>();
        path = new ArrayList<>();
        dfs(nums, 0);
        return ret;
    }

     递归三步      

  • 函数头

同样需要用到递归,但是在递归的过程中我们也需要时时知道当前下该数据对应的数组下标,所以在传参时我们需要不仅需要传入数组还需要传入一个下标。dfs(int[] nums, int pos) 

  • 函数体

主要的步骤就是对数组元素的选和不选,谁先谁后不重要,假如先选只需要将数组元素添加至path中记录,然后继续往下添加,当满足返回条件时就返回,返回后我们就需要记录不选时的path,所以需要删除已经添加的最后那个元素。

  • 递归出口

当传入的数组下标等于数组的长度时,将path传入ret中记录,最后返回。

    public void dfs(int[] nums, int pos){
        if (pos == nums.length){
            ret.add(new ArrayList<>(path));
            return;
        }
        path.add(nums[pos]);
        dfs(nums, pos+1);
        path.remove(path.size()-1);
        dfs(nums, pos+1);
    }

解法二:我们可以从0开始,选一个的时候,选完一个再往该下标后面继续选,直到不小于数组长度就不再继续。

该方法与之前的方法差距就在于这个方法遍历的次数可以少很多,对于这个方法来说它的每一个分叉结点都是结果,解法一来说,它就只能是叶子结点,对比而言解法二更高效。

该方法需要用到循环,循环从该下标+1开始,需要添加数据到path中,最后需要回复现场。

    public void dfs(int[] nums, int pos){
        ret.add(new ArrayList<>(path));
        for (int i = pos; i < nums.length; i++){
            path.add(nums[i]);
            dfs(nums, i+1);
            path.remove(path.size()-1);
        }
    }

相关文章:

  • WebGL图形编程实战【2】:动态着色 × 纹理贴图技术揭秘
  • 学习Flutter:搭建第一个 Flutter 应用
  • StarRocks 主键(Primary Key)深度解析
  • MATLAB+Arduino控制小车直行+转向
  • 竞赛团队招募系统----山东大学web课程设计
  • cool-admin-midway 使用腾讯云cos上传图片
  • SAP SD学习笔记35 - ATP(可用性检查)的各种Pattern
  • NLP高频面试题(十二)——Lora微调的原理、什么是Qlora
  • AWS NoSQL解决方案全景图
  • 【MySQL笔记】数据类型
  • 外设的中断控制
  • java设计模式之代理模式《赛博园丁的代理觉醒》
  • Mybatis注解的基础操作——02
  • 基于STM32单片机的智能手环/音乐播放/语音识别
  • RWKV-7:超越Transformer的新一代RNN架构解析
  • Vue3组件设计模式:高可复用性组件开发实战
  • 【数据结构】_单链表_相关面试题(一)
  • 使用欧拉法数值求解微分方程的 Python 实现
  • Windows 和 Linux 系统下,如何查看 Redis 的版本号?
  • 4.2、网络安全体系与建设内容
  • 国台办:实现祖国完全统一是大势所趋、大义所在、民心所向
  • 重庆发布经济犯罪案件接报警电子地图,企业可查询导航属地经侦服务点
  • 湖南省职业病防治院通报3岁女童确诊“铊中毒”:去年病例,编辑误写为“近日”
  • 虚假认定实质性重组、高估不良债权价值,原中国华融资产重庆分公司被罚180万元
  • 昆明阳宗海风景名胜区19口井违规抽取地热水,整改后用自来水代替温泉
  • 金融监管总局:近五年民企贷款投放年平均增速比各项贷款平均增速高出1.1个百分点