算法系列--递归,回溯,剪枝的综合应用(1)

💕"对相爱的人来说,对方的心意,才是最好的房子。"💕
作者:Lvzi
文章主要内容:算法系列–递归,回溯,剪枝的综合应用(1)
在这里插入图片描述

大家好,今天为大家带来的是算法系列--递归,回溯,剪枝的综合应用(1)

1.全排列(重点)

链接:
https://leetcode.cn/problems/permutations/description/
在这里插入图片描述

分析:

1.画出决策树

所谓的决策树就是我们小时候学排列画的树状图,通过一个树枚举出所有的情况
在这里插入图片描述
画出决策树之后,分析每一层干的事情是否相同(一般都是相同的),对于本题,每一层干的事可以总结为

  • 枚举出所有符合条件的排列情况

注意:决策树画的越详细越好(包括所有不符合条件的情况也画出来),有助于我们后面设计代码

2.设计代码

设计代码主要从两个方面考虑

  1. 全局变量
  2. dfs函数

1.全局变量

模拟决策的过程,想想需要哪些变量,首先题目要求的返回值是一个二维数组,所以需要设计一个ret作为返回值,当我们在枚举出所有的情况时,要考虑到枚举的数字是否被使用,如果被使用就不能被枚举,所以要标记搜索路径上数字的使用情况,所以要创建一个布尔类型的数组,接着当我们走到叶子结点时,需要保存当前排列的情况,一共就三个数字,所以需要使用一个数组进行保存,接着当从叶子结点向上返回时,我们需要舍弃掉数组中最后一个数字,这个操作比较简单,可以直接对数组进行变动即可

  • List<List> ret: 最后的返回值,用于记录所有排列情况
  • List path: 用于记录每一次dfs的结果
  • boolean[] check : 用于标记搜索过程中数字的使用情况

2.dfs函数

和递归相同,dfs函数的设计我们只需要关注某一个子问题的具体操作即可

把数组中所有的数都枚举一遍,只要没有用过,就添加到path后面

3.细节问题

  • 剪枝:在check中被标记为true,就进行剪枝

  • 回溯:如图
    在这里插入图片描述

  • 递归出口:当path中元素的数目和nums中元素的数目相同时,递归结束,将path中的所有元素添加到ret之中

4.代码实现

class Solution {// 全局变量List<List<Integer>> ret;// 返回值List<Integer> path;// 记录搜索路径boolean[] check;// 标记是否被使用过public List<List<Integer>> permute(int[] nums) {ret = new ArrayList<>();path = new ArrayList<>();check = new boolean[nums.length];dfs(nums);return ret;}public void dfs(int[] nums) {// 递归出口if(nums.length == path.size()) {ret.add(new ArrayList<>(path));return;}// 函数体for(int i = 0; i < nums.length; i++) {if(check[i] == false) {path.add(nums[i]);check[i] = true;dfs(nums);// 回溯check[i] = false;path.remove(path.size() - 1);}}}
}

为什么不是ret.add(path);

在这里插入图片描述

2.⼦集

链接:
https://leetcode.cn/problems/subsets/description/

分析:
画决策树:

根据定义选或者不选当前数
在这里插入图片描述

每一层都在干啥
分别枚举出选当前数字选和不选当前数的所有情况

设计代码:

全局变量:模拟整个过程,需要两个变量

  • ret:接收每次搜索的结果,是最终的返回值
  • path: 表示搜索路径的结果

dfs: 需要一个数组和当前的位置(下标),因为我需要知道当前的数是谁

细节问题:

  • 剪枝:不需要
  • 回溯:只有当选择选当前数的情况时,在返回的时候需要删除这个数
  • 递归出口:当pos走到n.length时表示数组遍历完毕,结束递归,将path添加进ret之中

代码:

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 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);}
}

这种决策树的遍历类似于二叉树遍历中的后序遍历

第二种决策树

在这里插入图片描述

以当前位置的值为起始位置,枚举出后面所有的数字的情况

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 pos) {// 这个遍历类似于前序遍历  根左右ret.add(new ArrayList<>(path));// 刚进来的时候path为空 空集也是子集的一种// 从当前位置一直遍历到结束for(int i = pos; i < nums.length; i++) {path.add(nums[i]);dfs(nums,i+1);path.remove(path.size() - 1);// 回溯}}
}

这种遍历的方式类似于二叉树遍历中的前序遍历,先打印根节点的值,再去遍历左右子树

总结:

  1. 画出具体详细的决策树,模拟每一步都是在干啥,明确操作
  2. 设计代码,具体来说是要设计出需要的全局变量和dfs,设计dfs和我们之前递归过程中设计函数头,函数体,递归出口一样,这不过这里的逻辑会更加的复杂一点
  3. 注意细节问题:主要从两个方面考虑
    • 剪枝
    • 回溯

3.找出所有⼦集的异或总和再求和

链接:
https://leetcode.cn/problems/sum-of-all-subset-xor-totals/

分析:
在这里插入图片描述

代码:

class Solution {// 全局变量int ret;int path;public int subsetXORSum(int[] nums) {dfs(nums,0);return ret;}public void dfs(int[] nums,int pos) {ret += path;for(int i = pos; i < nums.length; i++) {path ^= nums[i];dfs(nums,i + 1);// 递归下一个位置path ^= nums[i];// 回溯}}
}

注意这里面利用了^运算的性质,a ^ a = 0

还是那句话,画出决策树一切都好办!!!

四.全排列II

链接:
https://leetcode.cn/problems/permutations-ii/

分析:

相较于全排列I多了个限制条件不能有重复的组合出现,那么只需分析出所有的不合法的情况即可
1.

  1. 同一层中(同一位置比如选择第一个位置的数),不能选择重复的数字
  2. 和全排列I一样,不能选择已经使用过的数字

对于2的处理和全排列I的处理方式相同,使用一个布尔类型的check数组标记即可,对于1需要判断出 不合法的数据,首先要和前面的数字相同nums[i] == nums[i - 1],i不能越界i != 0,其次上述两个条件还不能完全判定出是不合法的数据,还必须要保证前一个数字是同一层的,check[i - 1] == false

总结来说就是:

  1. nums[i] == nums[i-1] && 在同一层–>不合法数据–>不能dfs
  2. nums[i] == nums[i-1] && 不在同一层–>合法数据–>能dfs

此外,为了保证相同的数据是紧挨着的,还需要进行排序

代码:

class Solution {// 全局变量List<List<Integer>> ret;// 返回值List<Integer> path;// 记录搜索路径boolean[] check;// 标记是否被使用过public List<List<Integer>> permuteUnique(int[] nums) {ret = new ArrayList<>();path = new ArrayList<>();check = new boolean[nums.length];Arrays.sort(nums);dfs(nums);return ret;}public void dfs(int[] nums) {// 递归出口if(path.size() == nums.length) {ret.add(new ArrayList<>(path));return;}// 函数体for(int i = 0; i < nums.length; i++) {// 剪枝  不合法的数据if(check[i] == true || (i != 0 && nums[i] == nums[i - 1] && check[i - 1] == false)) {continue;}path.add(nums[i]);check[i] = true;dfs(nums);// 回溯check[i] = false;path.remove(path.size() - 1);}}
}

5.电话号码的字⺟组合

链接:
https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
分析:

每一层所做的事情:

  • 枚举出当前数字对应的字符串中所有的字符

设计代码:
1.全局变量
ret:返回值
path
string[] map:映射关系

2.dfs(digits,pos)
pos表示digits中字符的下标
递归出口:
走到最后一个节点的位置

回溯:
删除最后添加的数字

剪枝:无

在这里插入图片描述
代码:

class Solution {List<String> ret;// 返回值StringBuffer path;// 记录搜索路径String[] map= {"","","abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};// 建立映射关系public List<String> letterCombinations(String digits) {ret = new ArrayList<>();path = new StringBuffer();if(digits.length() == 0) return ret;// 处理为空的情况dfs(digits,0);return ret;}private void dfs(String digits, int pos) {if(pos == digits.length()) {// 递归出口ret.add(path.toString());return;}String s = map[digits.charAt(pos) - '0'];// 获取当前层的字符串for(int i = 0; i < s.length(); i++) {path.append(s.charAt(i));// 追加字符dfs(digits, pos + 1);path.deleteCharAt(path.length() - 1);// 回溯}}
}

dfs是往下,是递归,每一层是要做的事情是子问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/785031.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Redis集合List实现消息队列

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型…

高阶DS---AVL树详解(每步配图)

目录 前言&#xff1a; AVL树的概念: AVL树节点的定义&#xff1a; AVL树的插入&#xff08;重点&#xff09; AVL树的旋转&#xff1a; &#xff08;1&#xff09;新节点插入较高左子树的左侧---右单旋 &#xff08;2&#xff09;新节点插入较高右子树的右侧---左单旋 …

unity双层滑动实现

实现功能&#xff1a; 当滑动列表中内容处于顶端的时候&#xff0c;向上滑动优先滑动整个滑动列表&#xff0c;当滑动列表移动到设置位置&#xff0c;即设定的最高处时&#xff0c;继续移动列表内内容。向下移动亦然&#xff0c;当内容处于滑动列表顶端时&#xff0c;移动整个滑…

深入解析Hadoop生态核心组件:HDFS、MapReduce和YARN

这里写目录标题 01HDFS02Yarn03Hive04HBase1&#xff0e;特点2&#xff0e;存储 05Spark及Spark Streaming关于作者&#xff1a;推荐理由&#xff1a;作者直播推荐&#xff1a; 一篇讲明白 Hadoop 生态的三大部件 进入大数据阶段就意味着进入NoSQL阶段&#xff0c;更多的是面向…

[游戏开发][UE5.3]代码生成蓝图文件并在代码中保存文件。

我看网上有人的做法比我更好&#xff0c;我这个更简单 UE5-GAS:读取Excel数据在蓝图创建并更新GE类 - 知乎 数据配表 测试编辑器API 创建编辑器蓝图文件&#xff0c;继承AssetActionUtility.h 创建在编辑器中显示的函数&#xff0c;可以用中文命名方便其他人使用。 右键任意…

从零开始学Python数据分析:菜鸟也能成高手(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【数据结构与算法初阶(c语言)】插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序-全梳理(万字详解,干货满满,建议三连收藏)

目录 1.排序的概念及其运用 1.1排序的概念 1.2排序运用 1.3常见的排序算法 2.插入排序 2.1 原理演示&#xff1a;​编辑 2.2 算法实现 2.3 算法的时间复杂度和空间复杂度分析 3.希尔排序 3.1算法思想 3.2原理演示 3.3代码实现 3.4希尔算法的时间复杂度 4.冒泡排序 4.1冒泡排…

test01

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

Java的IDEA的工程管理

模块和包的图标&#xff1a; 举个例子&#xff1a; IDEA中创建包&#xff1a; 如图所示&#xff0c;com.LBJ的意思是在com包中创建子包LBJ 参见&#xff1a; IDEA中项目、模块和包的关系_idea中模块和项目-CSDN博客

网站秒收录的6个方法

网站怎么做到秒收录 网站的收录速度对于网站的曝光和流量至关重要。如果能够实现网站的秒收录&#xff0c;将大大提高网站的可见性和访问量。下面介绍几种方法&#xff0c;帮助您实现网站秒收录的目标。 1. 提交sitemap.xml文件 创建并提交sitemap.xml文件是实现网站快速收录…

Netty学习——源码篇9 Netty的Handler其他处理 备份

1 ChannelHandlerContext 每个ChannelHandler被添加到ChannelPipeline后&#xff0c;都会创建一个ChannelHandlerContext&#xff0c;并与ChannelHandler关联绑定。ChannelHandlerContext允许ChannelHandler与其他的ChannelHandler进行交互。ChannelHandlerContext不会改变添加…

U8二次开发-钉钉集成

钉钉开放平台作为企业沟通和协作的重要工具,其技术的每一次迭代都为企业带来了新的机遇和挑战。随着企业对于高效沟通和智能化管理的需求日益增长,钉钉平台的SDK更新显得尤为重要。把传统的U8与钉钉平台集成,可以有效的将业务功能和角色进行前移,打破应用系统二八原则,即8…

dailyneaty、希亦、鲸立婴儿洗衣机怎么样?三款卷王测评PK对决

曾经我还是一直选择手洗婴儿衣物&#xff0c;最终还是加入了买婴儿洗衣机的大军&#xff0c;一方面因为我懒&#xff0c;不想再继续手洗&#xff0c;另一方面是因为我看了科普才知道&#xff0c;当我们清洗衣物时&#xff0c;除了要洗掉衣物表面的污渍&#xff0c;更需要消除掉…

MATLAB——知识点备忘

最近在攻略ADC建模相关方面&#xff0c;由好多零碎的知识点&#xff0c;这里写个备忘录。 Matlab 判断一个数是否为整数 1. isinteger 函数 MATLAB中&#xff0c;可以使用 isinteger 函数来判断一个数是否为整数&#xff0c;例如&#xff1a;要判断x是否为整数可以采用以下代…

科研学习|论文解读——情感对感知偶然信息遭遇的影响研究(JASIST,2022)

原文题目 Investigating the impact of emotions on perceiving serendipitous information encountering 一、引言 serendipity一词最初是由霍勒斯沃波尔创造的&#xff0c;他将其定义为“通过意外和睿智发现你并不追求的事物”。信息研究中大多数现有的偶然性定义从几个角度看…

劳动力规划:对企业加速运营的未来展望

近年来&#xff0c;企业面临着过山车般的经济形势&#xff0c;面对消费水平的上涨、市场波动带来的担忧以及数字化的加速转型&#xff0c;许多企业虽然对未来仍秉持着谨慎乐观的态度&#xff0c;但也同时认为自身缺乏持续增长和成功转型的能力。为了让企业能够实现战略目标、应…

算法笔记之蓝桥杯pat系统备考(3)

算法笔记之蓝桥杯&pat系统备考&#xff08;2&#xff09; 多训练、多思考、多总结٩(๑•̀ω•́๑)۶ 八、深搜和广搜 8.1DFS dfs是一种枚举完所有完整路径以遍历所有情况的搜索方法&#xff0c;可以理解为每次都是一条路走到黑的犟种。 以老朋友斐波那契额数列为例&a…

Docker-compose管理工具的使用

华子目录 容器编排工具docker composecompose介绍compose使用的三个步骤docker-compose.yml文件案例compose具有管理应用程序整个生命周期的命令 docker compose安装安装条件在Linux系统上安装composedocker compose卸载 docker compose运用演示修改compose配置&#xff0c;添加…

【手册】——mq延迟队列

目录 一、背景介绍二、思路&方案三、过程1.项目为啥用延迟队列&#xff1f;2.项目为啥用三方延迟队列&#xff1f;3.项目中为啥用rabbitmq延迟队列&#xff1f;4.rabbitmq延迟队列的安装5.rabbitmq的延迟队列配置方式5.1.exchange配置5.2.queues配置5.3.exchange和queues的…

初识C++ · 入门(2)

目录 1 引用 1.1引用的概念 1.2 引用的特性 2 传值&#xff0c;传引用的效率 3 引用和指针的区别 4 内联函数 4.1 内联函数的定义 4. 2 内联函数的特性 5 关键字auto 5.1关于命名的思考 5.2 关于auto的发展 5.3 auto使用规则 6 范围for的使用 7 空指针 1 引用 …