继续看回溯问题

关卡名

继续看回溯问题

我会了✔️

内容

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+2
                backTrack(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;
    }
 
    //每次迭代获取一个字符串,所以会设计大量的字符串拼接,所以这里选择更为高效的 StringBuild
    StringBuilder temp = new StringBuilder();
 
    //比如digits如果为"23",num 为0,则str表示2对应的 abc
    public 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);
        }
    }
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

垃圾回收 (GC) 在 .NET Core 中是如何工作的?(二)

接上一篇文章垃圾回收 (GC) 在 .NET Core 中是如何工作的&#xff1f;-CSDN博客 GC 会分配堆段&#xff0c;其中每个段都是一系列连续的内存。 置于堆中的对象归类为 3 个代系之一&#xff1a;0、1 或 2。 代系可确定 GC 尝试在应用不再引用的托管对象上释放内存的频率。 编号…

C++_构造函数与析构函数

目录 1、构造函数的写法 1.2 构造函数优化写法 2、默认构造函数与默认成员函数 2.1 默认成员函数对不同类型的处理 3、对内置类型的补丁 4、析构函数 4.1 析构函数的写法 5、默认析构函数 6、初始化列表 6.1 初始化列表的写法 6.2 初始化列表的作用 6.3 回顾与总结 …

引迈信息-JNPF平台怎么样?值得入手吗?

目录 1.前言 2.引迈低代码怎么样&#xff1f; 3.平台亮点展示 4.引迈产品特点 5.引迈产品技术栈&#xff1a; 1.前言 低代码是近几年比较火的一种应用程序快速开发方式&#xff0c;它能帮助用户在开发软件的过程中大幅减少手工编码量&#xff0c;并通过可视化组件加速应用…

高效电商策略:小红书集成CRM与广告推广无代码化

无代码开发的优势 随着科技的不断进步&#xff0c;无代码开发&#xff08;No-Code Development&#xff09;已经成为快速构建系统和应用的新趋势。无代码开发指的是不需要传统编程知识&#xff0c;通过图形化的用户界面和模型驱动逻辑来创建应用程序。这种方式让非技术背景的用…

排序 | 冒泡插入希尔选择堆快排归并计数排序

排序 | 冒泡插入希尔选择堆快排归并计数排序 文章目录 排序 | 冒泡插入希尔选择堆快排归并计数排序冒泡排序插入排序希尔排序选择排序堆排序快速排序--交换排序三数取中快速排序hoare版本快速排序挖坑法快速排序前后指针法 快速排序--非递归实现归并排序归并排序非递归实现非比…

Window操作系统发展史

引言 当谈及计算机操作系统的丰富历史和多样性时&#xff0c;Windows操作系统无疑是其中的一颗璀璨明星。自1985年首次亮相以来&#xff0c;Windows经历了长足的发展&#xff0c;塑造了计算机使用体验的方方面面。从初始的简单图形用户界面到如今强大而多样的功能&…

【开源工程及源码】数字孪生乡村—经典开源项目实景三维数字孪生

智慧乡村可视化平台&#xff0c;旨在通过数字化和智能化手段提升乡村管理、服务和发展水平。通过飞渡科技强大的渲染引擎&#xff0c;1&#xff1a;1 建模还原乡村全貌&#xff0c;建立起具备信息化、智能化、绿色化的智慧乡村综合管理平台。 综合态势模块下&#xff0c;可以从…

C语言——预处理详解(#define用法+注意事项)

#define 语法规定 #define定义标识符 语法: #define name stuff #define例子 #include<stdio.h> #define A 100 #define STR "abc" #define FOR for(;;)int main() {printf("%d\n", A);printf("%s\n", STR);FOR;return 0; } 运行结果…

模板方法模式(行为型)

目录 一、前言 二、模板模式 三、带钩子的模板模式 四、总结 一、前言 模板方法模式是一种行为型设计模式&#xff0c;它定义了一个操作中的算法框架&#xff0c;将一些步骤延迟到子类中实现。这种模式是基于“开闭原则”的设计思想&#xff0c;即对扩展开放&#xff0c;对…

【Unity动画】综合案例完结-控制角色动作播放+声音配套

这个案例实现的动作并不复杂&#xff0c;主要包含一个 跳跃动作、攻击动作、还有一个包含三个动画状态的动画混合树。然后设置三个参数来控制切换。 状态机结构如下&#xff1a; 完整代码 using System.Collections; using System.Collections.Generic; using UnityEngine;pu…

数据挖掘-08-基于Python实现时间序列分析建模(ARIMA 模型)(包括数据和代码)

文章目录 0. 数据代码下载1. 背景描述2. 预测目的3. 数据总览4. 数据预处理4.1数据描述性统计与清洗a. 导入程序库b. 读取数据c. 查看统计信息和空值d. 查看是否有重复数据以及清理重复数据e. 空值清理f. 针对清洗后的数据进行统计分析 5. 探索性数据分析5.1 数据分析 6. 构建 …

【2023年公司智能工具降本增效分享总结】「智能工具的力量」总结分享我司通过AI提升软件开发效率与质量调研报告,问题踩坑之路

调研背景 人工智能&#xff08;AI&#xff09;已经成为当今科技发展的主要驱动力之一&#xff0c;AI在多个领域取得了显著的成果&#xff0c;包括软件开发。AI技术的应用可以帮助开发者提高代码质量、减少错误、优化资源和时间管理&#xff0c;从而提高软件开发效率。 调研目…

Knowledge Graph知识图谱—9. Knowledge Modeling

9. Knowledge Modeling & Ontology Engineering How should the knowledge in a KG be modeled? – Which classes of entities do we have? – Which relations connect them? – Which constraints hold for them? → these questions are defined in the ontology …

javacv的视频截图功能

之前做了一个资源库的小项目&#xff0c;因为上传资源文件包含视频等附件&#xff0c;所以就需要时用到这个功能。通过对视频截图&#xff0c;然后作为封面缩略图&#xff0c;达到美观效果。 首先呢&#xff0c;需要准备相关的jar包&#xff0c;之前我用的是低版本的1.4.2&…

速学数据结构 | 树 森林 二叉树 的概念详讲篇

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! &#x1f4cb; 前言 &#x1f308;hello&#xff01; 各位宝子们大家好啊&#xff0c;关于线性表我们已经在前面更新完了…

【C++入门到精通】 线程库 | thread类 C++11 [ C++入门 ]

阅读导航 引言一、thread类的简单介绍二、线程函数详细介绍1. start() 函数&#xff08;1&#xff09;头文件&#xff08;2&#xff09;函数原型 2. join() 函数&#xff08;1&#xff09;头文件&#xff08;2&#xff09;函数原型 3. detach() 函数&#xff08;1&#xff09;头…

扫描电镜中的信号-噪声比(SNR)参数如何优化

在扫描电镜&#xff08;SEM&#xff09;中&#xff0c;信号-噪声比&#xff08;SNR&#xff09;的优化对于获得高质量的图像和可靠的数据分析至关重要。以下是一些优化SNR的方法&#xff1a; 选择适当的检测器&#xff1a;SEM通常配备了不同类型的检测器&#xff0c;如二次电子…

紫光展锐T820与飞桨完成I级兼容性测试 助推端侧AI融合创新

近日&#xff0c;紫光展锐高性能5G SoC T820与百度飞桨完成I级兼容性测试&#xff08;基于Paddle Lite工具&#xff09;。测试结果显示&#xff0c;双方兼容性表现良好&#xff0c;整体运行稳定。这是紫光展锐加入百度“硬件生态共创计划”后的阶段性成果。 本次I级兼容性测试完…

多域名https证书购买选择

多域名https证书是一种特殊的SSL证书&#xff0c;它允许一个证书同时保护多个域名&#xff0c;并且不限制域名的类型&#xff0c;可以保护多个域名和子域名&#xff0c;确保网站传输信息时不被窃取、篡改。那么我们该怎么选择符合需求的多域名https证书呢&#xff1f;今天就随S…

基于VGG-16+Android+Python的智能车辆驾驶行为分析—深度学习算法应用(含全部工程源码)+数据集+模型(一)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow 环境Pycharm 环境Android环境 相关其它博客工程源代码下载其它资料下载 前言 本项目采用VGG-16网络模型&#xff0c;使用Kaggle开源数据集&#xff0c;旨在提取图片中的用户特征&#xff0c;最终在移…