算法通过村第十八关-回溯|白银笔记|经典问题

文章目录

  • 前言
  • 组合总和问题
  • 分割回文串
  • 子集问题
  • 排序问题
  • 字母大小写全排列
  • 单词搜索
  • 总结


前言


提示:我不愿再给你写信了。因为我终于感到,我们的全部通信知识一个大大的幻影,我们每个人知识再给自己写信。 --安德烈·纪德

回溯主要解决一些暴力枚举也搞不定的问题,例如组合、分割、子集、排列、棋盘等。这关我们就看看如何接入。

组合总和问题

参考题目地址:39. 组合总和 - 力扣(LeetCode)

在这里插入图片描述
如过不考虑重复的,这个题目和113的那个题目相差无疑,如果可以重复,哪呢是否可以无限制取下去呢?也不会,因为题目也给了说明。每个元素最小为1,因此最多一个target个1.我们画图该怎么做呢,对于序列{2,3,6,7}.target = 7.很明显我们选择1个2,还剩下target = 7 - 2 = 5.然后再选取一个2,还剩下target = target - 2 = 3;之后再选一个2,变成了target = target - 2 = 1,此时最小的为2,不能再选了,就要退出。看看有没有3。ok,有那么第一个结果就是{2,2,3}.

之后我们继续回退到只选1个2的时候,这是2不能选了,从{3,6,7}中选择,如下图所示,没有符合规定的。

依次类推,然后尝试从3和6、7中开始选择。

最终的结果就是{2,2,3}和{2,5}.为了方便,我们可以先对元素做个排序,然后按照上面的过程执行,形成一个树形图:

在这里插入图片描述
这个图横向是针对每个元素做暴力枚举,纵向是递归(向下找)也是纵横问题,实现起来代码也不复杂:

public List<List<Integer>> combinationSum(int[] c, int target) {List<List<Integer>> res = new ArrayList();List<Integer> path = new ArrayList();dfs(c,0,target,path,res);return res;
}public void dfs(int[] c,int u,int target,List<Integer> path,List<List<Integer>> res){if(target < 0){return;}if(target == 0){res.add(new ArrayList(path));return;}for(int i = u; i < c.length; i++){if(c[i] <= target){path.add(c[i]);dfs(c,i,target - c[i],path,res);path.remove(path.size() - 1);}   }
}

分割回文串

参考题目地址:131. 分割回文串 - 力扣(LeetCode)

在这里插入图片描述
字符串如何判断回文本身就是一道算法题,本题在其之上还要解决一个问题:如何分割?如果暴力切割很麻烦,解决起来也很困难,如果从回溯的角度去思考就很清晰:
在这里插入图片描述
我们说回溯本身仍然回进行枚举的,这里也是一样的。切割线(途中的|)切割到字符串的结尾位置,说明找到了一个切割方法。这里就是先试一试,第一次切割’a’,第二次切割’aa‘,第三次切割’aab’。这对应的就是回溯里面的for循环(也就是横向遍历)

我们还要说回溯(需要进行递归操作)第一次切合’a’,剩下的就是‘ab’.递归就是在将其且下一个回文下来,也就是第二个‘a’,剩下的‘b’再交给递归进行一步切割。这就是纵向方面要干的事,依次类推。

至于回溯操作与前面的一样的道理,不再赘述。通过代码就可以发现,切割问题的回溯搜索的过程和嘴和问题的回溯搜索的过程是差不多的。

class Solution {List<List<String>> res = new ArrayList<>();Deque<String> path = new LinkedList<>();public List<List<String>> partition(String s) {backTracking(s,0);return res;}private void backTracking(String s, int startIndex) {// 如果起始位置大于s的大小,说明找到了一组分割方案if (startIndex >= s.length()) {res.add(new ArrayList<>(path));return;}for (int i = startIndex; i < s.length(); i++) {// 如果是回文子串,则记录if (isPalindrome(s, startIndex, i)) {String str = s.substring(startIndex, i + 1);path.addLast(str);} else {continue;} // 起始位置后移,保证不重复backTracking(s, i + 1);path.removeLast();}}public boolean isPalindrome(String str,int start,int end){for(int i = start, j = end; i < j; i++,j--){if(str.charAt(i) != str.charAt(j)){return false;}}return true;}
}

子集问题

子集问题也是回溯的经典使用场景。回溯可以画成一种状态树,子集,组合,分割,都可以抽象出来。

但是子集也有它特有的类型:需要找出所有情况。

参考题目:78. 子集 - 力扣(LeetCode)

在这里插入图片描述
在这里插入图片描述
画图详解:
在这里插入图片描述
从图中也可以看出,遍历这颗树的时候,就可以记录下所有的节点,也就是得到子集结果。

那么什么时候停下来呢?起始可以不加终止条件,因为startIdex >= nums.size(),for循环就结束了。

记住:求子集问题,不需要任何的剪枝操作!因为子集就是要遍历整颗树。这样实现起来也比较容易。

class Solution {List<List<Integer>> res = new ArrayList<>();List<Integer> path = new ArrayList();public List<List<Integer>> subsets(int[] nums) {// 空集也是if(nums.length == 0 ){res.add(new ArrayList<>());return res;}backTracking(nums,0);return res;} public void  backTracking(int[] nums,int startIndex){// 记住:加进来,再判断res.add(new ArrayList(path));if(startIndex >= nums.length){return;}for(int i = startIndex; i < nums.length; i++){// 今典小连招path.add(nums[i]);backTracking(nums,i+ 1);path.remove(path.size() - 1);}}
}

tips:上面的代码可以通过全局定义res和path,这样简介。这里(也可以转成参数传递的形式)

排序问题

参考题目介绍:46. 全排列 - 力扣(LeetCode)

在这里插入图片描述

排列问题也是经典的问题(每次刷是不是都难)。这个问题和前面的组合等问题的一个区别就是后面的还用再选(可重复),例如:1:开始使用了,但是到了2和3的时候仍然要使用一次。本质上是因为【1,2】和【2,1】从集合的角度看一个问题,单是从排列的角度是两个问题。

元素1在【1,2】中已经使用多了,但是在【2,1】中还要再使用一次,所以就不能使用startIndex了,此时可以使用一个used数组来标记已经选择的元素,完整的过程如图所示:

在这里插入图片描述
开图就知道了,怎么确定终止条件呢?就是到达叶子节点,那么是么时候到达叶子节点呢?

当时收集元素的数组path的大小达到和nums数组一样不就行了,说明就找到了一个全排列,也就是到达叶子节点了。

class Solution {List<List<Integer>> res = new ArrayList<>();List<Integer> path = new ArrayList<>();// 是否选boolean[] used ; public List<List<Integer>> permute(int[] nums) {if(nums.length == 0){return res;}used = new boolean[nums.length];permuteHepler(nums);return res;}public  void permuteHepler(int[] nums){if(path.size() == nums.length){res.add(new ArrayList<>(path));return ;}for(int i = 0; i < nums.length; i++){//  解决01 10 问题if(used[i]){continue;}used[i] = true;path.add(nums[i]);permuteHepler(nums);//  经典回溯path.remove(path.size() - 1);used[i] = false;}}
}

字母大小写全排列

参考题目地址:784. 字母大小写全排列 - 力扣(LeetCode)

在这里插入图片描述

如果本题去掉数组,只告诉你两个字母ab,然你每个字母变化大小写,那么就是ab,Ab,aB,AB四种情况。就和上面的处理一样了。这里有数字的干扰,我们就需要作过滤数字,只处理字母。还有就是添加大小写的转换问题了。
在这里插入图片描述

由于每个字符的大小形式刚好相差32,因此再大小写里面可以换成用c^32来进行转换和恢复。这样代码就简洁多了:

class Solution {List<String> res = new ArrayList<>();public List<String> letterCasePermutation(String s) {dfs(s.toCharArray(),0);return res;}public void dfs(char[] arr, int pos){// 过滤数字while(pos < arr.length && Character.isDigit(arr[pos])){pos++;}// 终止条件if(pos == arr.length){res.add(new String(arr));return;}// 转换arr[pos]^=32;dfs(arr,pos + 1);// 撤销arr[pos]^=32;dfs(arr,pos + 1);}
}

单词搜索

参考题目介绍:79. 单词搜索 - 力扣(LeetCode)

在这里插入图片描述

在这里插入图片描述

思路:从上到下,左到右遍历网格,每个坐标递归调用check(i,j,k)函数,i,j表示网格坐标,k表示word的第k个字符,如果能搜索到第k个字符返回true,否则返回false。

check函数的终止条件也很明确:

  1. 如果i,j的位置和字符上的字符位置k不相符,也就是说这个方面的路径有问题,直接返回false
  2. 如果搜索到了字符串的结尾,则找到了网格中的一条路径,这条路径上的字符正好可以组成字符串s,返回true。

两种情况都不满足,就把当前网格加入visited数组,visited表示该位置已经访问过了,然后顺着当前网格的坐标的四个方向继续尝试,如果没有找到k开始的子串,则返回回溯状态visited[i ] [j] = false,继续向后面访问。

分析一波复杂度:时间复杂度为O(ML*3^L),M,N 为网格的长度和宽度,L 为字符串word的长度,第一次电泳check函数的时候,回向4个方向进行测试,其余坐标的节点都是3个方向检车,走过的分支不会反方向回去,左移check函数的复杂度为3^L,而网格上面有MN个坐标,且存在剪枝,最坏的情况下是O(MN * 3 ^L).空间复杂符是O(MN) visited数组的空间是O(MN) ,check递归栈的最大深度在最坏的情况下是O(MN)

class Solution {public boolean exist(char[][] board, String word) {char[] words = word.toCharArray();for(int i = 0; i < board.length; i++){for(int j  = 0; j < board[i].length;j++){if(dfs(board,words,i,j,0)){return true;}}}return false;}public boolean dfs(char [][] board,char[] words,int i,int j, int k){// 边界+条件if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != words[k]){return false;}// 终止条件if(k ==  words.length - 1){return true;}// 防止重复board[i][j] = '\0';boolean res = dfs(board,words,i+1,j,k+1) || dfs(board,words,i - 1,j,k+1) || 							dfs(board,words,i,j - 1,k+1)  ||  dfs(board,words,i,j+ 1,k+1);// 恢复board[i][j] = words[k];return res;}
}

总结

提示:组合总和问题;分割问题;子集问题;排列问题;搜索问题


如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~

也欢迎你 关注我 ,喜欢交朋友,喜欢一起探讨问题。

在这里插入图片描述

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

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

相关文章

5-爬虫-打码平台、打码平台自动登录打码平台、selenium爬取京东商品信息、scrapy介绍安装、scrapy目录结构

1 打码平台 1.1 案例 2 打码平台自动登录打码平台 3 selenium爬取京东商品信息 4 scrapy介绍安装 5 scrapy目录结构 1 打码平台 # 1 登录某些网站&#xff0c;会有验证码---》想自动破解-数字字母&#xff1a;python模块&#xff1a;ddddocr-计算题&#xff0c;成语题&#xf…

在现实生活中传感器GV-H130/GV-21的使用

今天&#xff0c;收获了传感器GV-H130/GV-21&#xff0c;调试探头的用法&#xff0c;下面就来看看吧&#xff01;如有不妥欢迎指正&#xff01;&#xff01;&#xff01;&#xff01; 目录 传感器GV-H130/GV-21外观 传感器调试探头 探头与必要准备工作 传感器数值更改调试 …

持续集成交付CICD:Jenkins Pipeline与远程构建触发器

目录 一、实验 1.Jenkins Pipeline本地构建触发器 2.Jenkins Pipeline与远程构建触发器&#xff08;第一种方式&#xff09; 3.Jenkins Pipeline与远程构建触发器&#xff08;第二种方式&#xff09; 4.Jenkins Pipeline与远程构建触发器&#xff08;第三种方式&#xff0…

Java数据的基本(原始)类型和引用类型的特点差别

本文作为“Java数据类型”一文的补充https://blog.csdn.net/cnds123/article/details/110517272 Java的数据类型可以分为基本类型&#xff08;primitive types&#xff09;和引用类型&#xff08;reference types&#xff09;两大类。在实际编程中&#xff0c;要根据需求选择合…

5 ip的分配

如上一节所述&#xff0c;需要和其他设备通信&#xff0c;那么需要先配置ip. 1、如何配置ip 1.可以使用 ifconfig&#xff0c;也可以使用 ip addr 2.设置好了以后&#xff0c;用这两个命令&#xff0c;将网卡 up 一下&#xff0c;就可以了 //---------------------------- 使…

简述扫码登录原理及测试要点

扫码登录本质是解决将APP端的用户登录信息&#xff08;通常是Token&#xff09;通过扫码的形式安全稳定地同步给Web端。 操作流程&#xff1a; 打开登录页面&#xff0c;展示一个二维码(web)&#xff1b;打开APP扫描该二维码后&#xff0c;APP显示确认、取消按钮(app)&#xf…

Flink之状态管理

Flink状态管理 状态概述状态分类 键控、按键分区状态概述值状态 ValueState列表状态 ListStateMap状态 MapState归约状态 ReducingState聚合状态 Aggregating State 算子状态概述列表状态 ListState联合列表状态 UnionListState广播状态 Broadcast State 状态有效期 (TTL)概述S…

pytorch(小土堆)深度学习

第五节课讲项目的创建和对比 第六节&#xff1a;Dataset,Dataloader Dataset提供一种方式区获取数据及其label(如何获取每一个数据及其label&#xff0c;告诉我们总共有多少的数据) Dataloader为后面的网络提供不同的数据形式 第七节&#xff1a;Dataset类代码实战 显示图片 f…

WebSocket在node端和客户端的使用

摘要 如果想要实现一个聊天的功能&#xff0c;就会想到使用WebSocket来搭建。那如果没有WebSocet的时候&#xff0c;我们会以什么样的思路来实现聊天功能呢&#xff1f; 假如有一个A页面 和 B页面进行通信&#xff0c;当A发送信息后&#xff0c;我们可以将信息存储在文件或者…

安防监控EasyCVR视频汇聚平台无法接入Ehome5.0是什么原因?该如何解决?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、云存储、回放…

2023.11.09 homework (2)

【七年级上数学】 教别人也是教自己&#xff0c;总结下&#xff1a; 13&#xff09;找规律的题目&#xff0c;累加题目&#xff0c;要整体看&#xff0c;不然不容易算出来&#xff0c;求最大值&#xff0c;那么就是【最大值集群和】减去【最小集群和】就是最大值 9-12&#x…

模态对话框和非模态对话框

创建到堆区这样非模态对话框就不会一闪而过 .exec使程序进入阻塞状态 ()[]{}lambda表达式 55号属性可以在对话框关闭的时候将堆区的内存释放掉从而防止内存泄露

在linux安装单机版hadoop-3.3.6

一、下载hadoop https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/core/hadoop-3.3.6/ 二、配置环境变量 1、配置java环境变量 2、配置hadoop环境变量 export HADOOP_HOME/usr/local/bigdata/hadoop-3.3.6 export HBASE_HOME/usr/local/bigdata/hbase-2.5.6 export JA…

手术训练系统项目

★ 手术训练系统项目 项目描述&#xff1a;手术训练系统&#xff0c;它提供了多项功能&#xff0c;包括账户登录与创建、数据库与账户管理、课程管理、小组管理、成绩统计、证书发布、训练和系统设置。 职责描述: 1、训练功能开发&#xff08;任务概述、任务指导、评分规则、评…

71 内网安全-域横向网络传输应用层隧道技术

目录 必备知识点&#xff1a;1.代理和隧道技术区别?2.隧道技术为了解决什么?3.隧道技术前期的必备条件? 演示案例:网络传输应用层检测连通性-检测网络层ICMP隧道Ptunnel使用-检测利用传输层转发隧道Portmap使用-检测,利用传输层转发隧道Netcat使用-检测,利用,功能应用层DNS隧…

Jmeter分布式性能测试细节+常见问题解决,资深老鸟带你避坑...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Jmeter分布式测试…

【TiDB】TiDB CLuster部署

目录 0 大纲 一 集群部署工具TiUP简介 1 TiUP 简介 2 TiUP使用 3 TiUP使用举例 二 TiDB Cluster安装配置需求 1 生产环境硬件需求 2 操作系统需求 三 TIDB部署 1 软硬件需求以及前置检查​编辑 2 安装TiUP 组件 ​3 集群拓扑文件 4 执行部署命令 &#xff08;1&…

6-爬虫-scrapy解析数据(使用css选择器解析数据、xpath 解析数据)、 配置文件

1 scrapy解析数据 1.1 使用css选择器解析数据 1.2 xpath 解析数据 2 配置文件 3 整站爬取博客–》爬取详情–》数据传递 scrapy 爬虫框架补充 # 1 打码平台---》破解验证码-数字字母&#xff1a;ddddocr-计算题&#xff0c;滑块&#xff0c;成语。。。-云打码&#xff0c;超…

HK WEB3 MONTH Polkadot Hong Kong 火热报名中!

HK Web3 Month 11月除了香港金融科技周外&#xff0c;HK Web3 Month又是一大盛事&#xff0c;从10月29日开始开幕直到11月18日结束。此次将齐聚世界各地的Web3产业从业者、开发者、社群成员和学生来参与本次盛会。除外&#xff0c;超过75位产业知名的讲者与超过50场工作坊将为…

大数据毕业设计选题推荐-农作物观测站综合监控平台-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…