继续看回溯问题

关卡名

继续看回溯问题

我会了✔️

内容

1.复习递归和N叉树,理解相关代码是如何实现的

✔️

2.理解回溯到底怎么回事

✔️

3.掌握如何使用回溯来解决二叉树的路径问题

✔️

1 复原IP地址 

这也是一个经典的分割类型的回溯问题。LeetCode93.有效IP地址正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是有效 IP 地址,但是 "0. 011. 255 .245"、"192.168.1.312" 和 "192.168@1.1" 是无效IP地址。给定一个只包含数字的字符串s,用以表示一个IP地址,返回所有可能的有效IP地址,这些地址可以通过在s中插入 '.' 来形成。你不能重新排序或删除s中的任何数字。你可以按任何顺序返回答案。

示例1:

输入:s = "25525511135"

输出:["255.255.11.135","255.255.111.35"]

该问题的思路与与前面的分割回文串基本一致,也是切割问题。回溯的第一步就是使用枚举将所有可能性搜出来,找到一个符合要求的就先切下来,后面的部分继续进行枚举和切割,如果到了最后发现不符合要求,则开始回溯。
本题的难度明显比上一题要大,主要是判断是否合法的要求更高了,比如第一个元素我们可以截取2、25、255、2552,很显然到了2552之后就不合法了,此时就要回溯。后面也一样,假如我们第一层截取的是2,第二层就从”5525511135“中截取,此时可以有5,55,552,显然552已经不合法了,依次类推。画出图来就如下所示: 

 

当然这里还要判断是0的情况等等,在字符串转换成数字一章,我们讲解了很多种要处理的情况,为此我们可以写一个方法单独来执行相关的判断。代码如下:

// 判断字符串s在左闭⼜闭区间[start, end]所组成的数字是否合法private Boolean isValid(String s, int start, int end) {if (start > end) {return false;}// 0开头的数字不合法if (s.charAt(start) == '0' && start != end) { return false;}int num = 0;for (int i = start; i <= end; i++) {// 遇到⾮数字字符不合法if (s.charAt(i) > '9' || s.charAt(i) < '0') { return false;}num = num * 10 + (s.charAt(i) - '0');if (num > 255) { // 如果⼤于255了不合法return false;}}return true;}

另外,IP地址只有四段,不是无限分割的,因此本题只会明确的分成4段,不能多也不能少。所以不能用切割线切到最后作为终止条件,而是分割的段数到了4就必须终止。考虑到我们构造IP地址时还要手动给添加三个小数点,所以我们用变量pointNum来表示小数点数量,pointNum为3说明字符串分成了4段了。
要手动添加一个小数点,这要增加一个位置来存储,所以下一层递归的startIndex要从i+2开始。其他的主要工作就是递归和回溯的过程了。这里的撤销部分要注意将刚刚加入的分隔符删掉,并且pointNum也要-1,完整代码如下: 

class RestoreIpAddresses {List<String> result = new ArrayList<>();public List<String> restoreIpAddresses(String s) {//这个是IP的特性决定的if (s.length()<4||s.length() > 12) return result; backTrack(s, 0, 0);return result;}// startIndex: 搜索的起始位置, pointNum:添加小数点的数量private void backTrack(String s, int startIndex, int pointNum) {if (pointNum == 3) {// 小数点数量为3时,分隔结束// 判断第四段⼦字符串是否合法,如果合法就放进result中if (isValid(s,startIndex,s.length()-1)) {result.add(s);}return;}for (int i = startIndex; i < s.length(); i++) {if (isValid(s, startIndex, i)) {//在str的后⾯插⼊⼀个小数点s = s.substring(0, i + 1) + "." + s.substring(i + 1);    pointNum++;// 插⼊小数点之后下⼀个⼦串的起始位置为i+2backTrack(s, i + 2, pointNum);pointNum--;// 撤销操作s = s.substring(0, i + 1) + s.substring(i + 2);//撤销操作} else {break;}}}
}

2 电话号码问题 

LeetCode17.电话号码组合问题,也是热度非常高的一个题目,给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母,9对应四个字母。

示例1:

输入:digits = "2| 3 4567"

输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

我们说回溯仍然会存在暴力枚举的情况,这个题就很典型,例如如果输入23,那么,2就有 a、b、c三种情况,3有d、e、f三种情况。组合一下就一共就有3*3=9种,如果是233,那么就是27种。
这里要注意的9对应4个字母,而1则没有,那该怎么建立字母和数字之间的映射呢?我们用一个数组来保存,而不写一堆的if else。而为了保证遍历时index也恰好与数组的索引一致,我们按照如下的方式来定义数组:
String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
接下来我们就用回溯来解决n层循环的问题,输入23对应的树就是这样的: 

树的深度就是输入的数字个数,例如输入23,树的深度就是2。而所有的叶子节点就是我们需要的结果["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]。所以这里的终止条件就是,如果当前执行的index 等于输入的数字个数(digits.size)了。
使用for循环来枚举出来,然后循环体内就是回溯过程了。基本实现过程如下:

class LetterCombinations {List<String> list = new ArrayList<>();public List<String> letterCombinations(String digits) {if (digits == null || digits.length() == 0) {return list;}//初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};backTracking(digits, numString, 0);return list;}//每次迭代获取一个字符串,所以会设计大量的字符串拼接,所以这里选择更为高效的 StringBuildStringBuilder temp = new StringBuilder();//比如digits如果为"23",num 为0,则str表示2对应的 abcpublic void backTracking(String digits, String[] numString, int num) {//遍历全部一次记录一次得到的字符串if (num == digits.length()) {list.add(temp.toString());return;}//str 表示当前num对应的字符串String str = numString[digits.charAt(num) - '0'];for (int i = 0; i < str.length(); i++) {temp.append(str.charAt(i));backTracking(digits, numString, num + 1);//剔除末尾的继续尝试temp.deleteCharAt(temp.length() - 1);}}
}

 3 括号生成问题

本题是一道非常典型的回溯问题,LeetCode22.数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例1:

输入:n = 3

输出:["((()))","(()())","(())()","()(())","()()()"]

要解决该问题,我们首先要明确一个问题,左右括号什么时候可以获得。我们知道左括号出现的数量一定等于n,观察题目给的示例,只要剩余左括号的数量大于0,就可以添加"(",例如"("、"(()"、”(()(“、”()((“都是可以的,因为我们只要再给加几个右括号就行,例如可以将其变成"()"、"(())"、”(()()“、”()(())“。
那添加右括号的要求呢?结论是:序列中左括号的数量必须大于右括号的数量,例如上面的"("、"(()"、”(()(“、”()((“,都可以,但是如果")"、"())"、”)()(“、”()((“就不可以了,此时的情况都是右括号数量大于等于左括号。所以我们可以得到两条结论:

  • 1.只要剩余左括号的数量大于0,就可以添加"("。
  • 2.序列中左括号的数量必须大于右括号的数量才可以添加"(",并且")"的剩余数量大于0。

接下来看如何用回溯解决,我们将添加"("视为left,将")"视为right,这样"("和")"各可以出现n次。我们以n=2为例画一下的图示:

 

图中红叉标记的位置是左括号大于有括号了,不能再向下走了,这个操作也称为剪枝。通过图示,可以得到如下结论:

  • 当前左右括号都有大于 0 个可以使用的时候,才产生分支,否则就直接停止。
  • 产生左分支的时候,只看当前是否还有左括号可以使用;
  • 产生右分支的时候,除了要求还有右括号,还要求剩余右括号数量一定大于左括号的数量;
  • 在左边和右边剩余的括号数都等于 0 的时候结束。

而且,从上图可以看到,不管是剪枝还是得到一个结果,返回的过程仍然可以通过回溯来实现: 

class GenerateParenthesis {public List<String> generateParenthesis(int n) {List<String> ans = new ArrayList<String>();backtrack(ans, new StringBuilder(), 0, 0, n);return ans;}/*** @param ans 当前递归得到的结果* @param cur 当前的括号串* @param open   左括号已经使用的个数* @param close  右括号已经使用的个数* @param max    序列长度最大值*/public void backtrack(List<String> ans, StringBuilder cur, int open, int close, int max) {if (cur.length() == max * 2) {ans.add(cur.toString());return;}//本题需要两次回溯,比较少见的情况if (open < max) {cur.append('(');backtrack(ans, cur, open + 1, close, max);cur.deleteCharAt(cur.length() - 1);}if (close < open) {cur.append(')');backtrack(ans, cur, open, close + 1, max);cur.deleteCharAt(cur.length() - 1);}}
}

 

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

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

相关文章

sourcetree使用详解

介绍 SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端管理工具&#xff0c;同时也是Mn版本控制系统工具。支持创建、克隆、提交、push、pull 和合并等操作。——百度百科 是一款比较好用的图形化GUI的git、hg管理工具。还有一些其他的可视化代码管理工具&#x…

vivado约束方法7

输出延迟 与“输入延迟”步骤类似&#xff0c;“定时约束”向导分析所有输出的路径端口&#xff0c;以识别其在设计内部的源时钟及其活动边缘。模板选择规则与输入延迟中描述的相同。下图显示了几个由向导提出并由用户部分编辑的输出约束。 对于每个约束&#xff0c;可以编辑三…

zync spi flash 频率配置

spi flash的频率配置 代码流程及最终的频率值。 驱动目录 基于4.14.55 内核&#xff0c; \drivers\spi\spi-dw-fmsh.c (控制器) \drivers\spi\spi-dw.c \drivers\mtd\devices\m25p80.c &#xff08;设备&#xff09; \drivers\spi\spi.c spi dts配置说明 spi0: spie000100…

2. 学成在线 - 项目搭建

文章目录 一、基础工程搭建1.1 构建父工程1.2 构建基础工程 二、数据库环境2.1 内容管理content数据库 三、模块需求分析介绍四、部署前端和系统管理服务 一、基础工程搭建 整个项目分为三大类工程&#xff1a;父工程、基础工程 和微服务工程。 父工程&#xff1a;xuecheng-pl…

Android Studio 实现音乐播放器

目录 一、引言 视频效果展示&#xff1a; 1.启动页效果 2.登录页效果 3.注册页效果 4.歌曲列表页效果 5.播放页效果 二、详细设计 1.登陆注册功能 2.音乐列表页面 2.音乐播放功能 三、源码获取 一、引言 Android初学者开发第一个完整的实例项目应该就属《音乐播放器…

Docker中的常见命令

Docker开机自启 systemctl enable dockerDocker容器开机自启 docker update --restartalways [容器名/容器id]案例&#xff1a;docker操作nginx 拉取Nginx镜像 docker pull nginx查看镜像 docker images创建并运行Nginx容器 docker run -d --name nginx -p 80:80 nginx查…

LT8711HE方案《任天堂Switch底座方案》

LT8711HE Type-c转HDMI方案 LT8711HE是高性能的Type-C/DP1.2转HDMI2.0转换器&#xff0c;设计用于连接 USB Type-C 源或 DP1.2 源到 HDMI2.0 接收器。该LT8711HE集成了符合 DP1.2 标准的接收器和符合 HDMI2.0 标准的发射器。此外&#xff0c;两个 CC 控制器是包括用于 CC 通信以…

1.6 实战:Postman请求Get接口-获取用于登录的图形验证码

上一小节我们学习了Postman的布局,对Postman有了一个整体的认知,本小节我们就来实操一下Get接口。 我们打开Postman,点击我们之前创建的请求”获取登录页验证码“。我们在地址栏里填入获取登录页验证码的接口地址。怎么查看这个接口地址呢?我们打开校园二手交易系统,打开…

Leetcode—2414.最长的字母序连续子字符串的长度【中等】

2023每日刷题&#xff08;六十&#xff09; Leetcode—2414.最长的字母序连续子字符串的长度 实现代码 class Solution { public:int longestContinuousSubstring(string s) {int ans 1;int t 1;for(int i 1; i < s.size(); i) {if(s[i] - s[i - 1] 1) {t;ans max(an…

师兄啊师兄第二季开播:男主成海神?玄机是懂联动的!

《师兄啊师兄》动画第二季在12月14日终于正式开播&#xff0c;首播两集&#xff0c;还是很有诚意的。 这部动画改编自言归正传的小说《我师兄实在太稳健了》&#xff0c;原著的知名度不算很高&#xff0c;但玄机制作的动画让这个IP火出了圈。 动画第一季就凭借高颜值的人物建模…

spring boot 实现直播聊天室

spring boot 实现直播聊天室 技术方案: spring bootwebsocketrabbitmq 使用 rabbitmq 提高系统吞吐量 引入依赖 <dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.42&…

LeetCode 300最长递增子序列 674最长连续递增序列 718最长重复子数组 | 代码随想录25期训练营day52

动态规划算法10 LeetCode 300 最长递增子序列 2023.12.15 题目链接代码随想录讲解[链接] int lengthOfLIS(vector<int>& nums) {//创建变量result存储最终答案,设默认值为1int result 1;//1确定dp数组&#xff0c;dp[i]表示以nums[i]为结尾的子数组的最长长度ve…

VS Code连接远程Linux服务器调试C程序

1.在 VS Code 上安装扩展 C/C 2.通过 VS Code 连接远程 Linux 服务器 3.通过 VS Code 在远程 Linux 服务器上安装扩展 C/C 4.打开远程 Linux 服务器上的文件夹 【注】本文以 /root/ 为例。 5.创建项目文件夹&#xff0c;并在项目文件夹下创建C程序 6.按 F5&#xff0c;选…

mysql中的int(1)和int(10)的区别

今天偶然发现同事在写sql建表的时候把int类型括号后面的数字写成了1&#xff0c;但是我发现数据库里面的值已经远远超过了1位所能表示的范围&#xff0c;所以括号里面的数字肯定不是表示长度了&#xff08;印象中早期的navivat建表的时候&#xff0c;int类型如果默认不指定长度…

devc++如何建立一个c++项目?devc++提示源文件未编译?

打开devc APP后是这样的界面&#xff1b; 点击文件-> 新建->项目&#xff0c;这一点应该不难&#xff0c;主要是最后这个选择什么&#xff1f; 这样即可。 devc提示源文件未编译&#xff1f; 点击工具->编译选项&#xff1b; 如果不能解决&#xff0c;那就是可能路径…

文物数字化建模纹理贴图

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 1、文物3D数字化建模的特点 文物埋在地下历经千年&#xff0c;由于时…

转动惯量与惯性张量 的推导

从牛顿第二定律推出绕固定轴旋转的转动惯量&#xff0c;再用类似方法从牛顿第二定律推出绕固定点转动的惯性张量 基础定义 角速度 ω \omega ω是一个三维向量&#xff0c;方向表示旋转轴&#xff0c;用右手定则代表旋转方向&#xff0c;长度代表旋转弧度的速度 线速度&#…

WebGL+Three.js入门与实战——给画布换颜色、绘制一个点、三维坐标系

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

为了吃鸡苦练狙击,避免坑队友自己造一个狙击游戏!

引言 一文教会你造一个简易的狙击游戏。 说到狙击&#xff0c;相信大家都不陌生&#xff0c;无论是影视作品还是网络游戏&#xff0c;都经常能看到狙击枪的身影&#xff0c;最深刻的是它能够从百里之外&#xff0c;一枪爆头。 本文将介绍如何在Cocos Creator中造一个简易的狙…

UE5 动画 Sequencer-学习笔记

P2. 课程介绍 资料&#xff1a;https://www.bilibili.com/video/BV1Ag411873f?p2&vd_source707ec8983cc32e6e065d5496a7f79ee6 Sequencer不仅可以做互动动画&#xff0c;还可以导出视频与序列帧 P3-4. 界面介绍 https://www.bilibili.com/video/BV1Ag411873f?p3&spm_…