【递归、搜索和回溯算法】专题三 :穷举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);
        }
    }
相关推荐
大刀爱敲代码44 分钟前
基础算法01——二分查找(Binary Search)
java·算法
追风少年1553 小时前
常见中间件漏洞之一 ----【Tomcat】
java·中间件·tomcat
yang_love10113 小时前
Spring Boot 中的 @ConditionalOnBean 注解详解
java·spring boot·后端
郑州吴彦祖7724 小时前
【Java】UDP网络编程:无连接通信到Socket实战
java·网络·udp
spencer_tseng4 小时前
eclipse [jvm memory monitor] SHOW_MEMORY_MONITOR=true
java·jvm·eclipse
鱼樱前端4 小时前
mysql事务、行锁、jdbc事务、数据库连接池
java·后端
Hanson Huang5 小时前
23种设计模式-外观(Facade)设计模式
java·设计模式·外观模式·结构型设计模式
HR Zhou5 小时前
群体智能优化算法-正弦余弦算法(Sine Cosine Algorithm, SCA,含Matlab源代码)
算法·机器学习·matlab·优化·群体智能优化
自信的小螺丝钉5 小时前
Leetcode 378. 有序矩阵中第 K 小的元素 二分查找
算法·leetcode·矩阵·二分查找
Hanson Huang5 小时前
23种设计模式-生成器(Builder)设计模式
java·设计模式·生成器模式