代码随想录二刷 day28 | 回溯 之 93.复原IP地址 78.子集 90.子集II

news2024/11/23 11:10:21

day28

      • 93.复原IP地址
        • 判断子串是否合法
      • 78.子集
        • 回溯三部曲
      • 90.子集II

93.复原IP地址

题目链接
解题思路: 切割问题就可以使用回溯搜索法把所有可能性搜出来
在这里插入图片描述回溯三部曲

  • 递归参数

startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。

本题我们还需要一个变量pointNum,记录添加逗点的数量。

所以代码如下:

vector<string> result;// 记录结果
// startIndex: 搜索的起始位置,pointNum:添加逗点的数量
void backtracking(string& s, int startIndex, int pointNum) {
  • 递归终止条件

终止条件和131.分割回文串

情况就不同了,本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。

pointNum表示逗点数量,pointNum为3说明字符串分成了4段了。

然后验证一下第四段是否合法,如果合法就加入到结果集里

代码如下:

if (pointNum == 3) { // 逗点数量为3时,分隔结束
    // 判断第四段子字符串是否合法,如果合法就放进result中
    if (isValid(s, startIndex, s.size() - 1)) {
        result.push_back(s);
    }
    return;
}
  • 单层搜索的逻辑

在131.分割回文串中已经讲过在循环遍历中如何截取子串。

for (int i = startIndex; i < s.size(); i++)循环中 [startIndex, i] 这个区间就是截取的子串,需要判断这个子串是否合法。

如果合法就在字符串后面加上符号.表示已经分割。

如果不合法就结束本层循环,如图中剪掉的分支:

93.复原IP地址
然后就是递归和回溯的过程:

递归调用时,下一层递归的startIndex要从i+2开始(因为需要在字符串中加入了分隔符.),同时记录分割符的数量pointNum 要 +1。

回溯的时候,就将刚刚加入的分隔符. 删掉就可以了,pointNum也要-1。

代码如下:

for (int i = startIndex; i < s.size(); i++) {
    if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法
        s.insert(s.begin() + i + 1 , '.');  // 在i的后面插入一个逗点
        pointNum++;
        backtracking(s, i + 2, pointNum);   // 插入逗点之后下一个子串的起始位置为i+2
        pointNum--;                         // 回溯
        s.erase(s.begin() + i + 1);         // 回溯删掉逗点
    } else break; // 不合法,直接结束本层循环
}

判断子串是否合法

最后就是在写一个判断段位是否是有效段位了。

主要考虑到如下三点:

  • 段位以0为开头的数字不合法
  • 段位里有非正整数字符不合法
  • 段位如果大于255了不合法

代码如下:

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

可以写出如下回溯算法C++代码:

class Solution {
private:
    vector<string> result;// 记录结果
    // startIndex: 搜索的起始位置,pointNum:添加逗点的数量
    void backtracking(string& s, int startIndex, int pointNum) {
        if (pointNum == 3) { // 逗点数量为3时,分隔结束
            // 判断第四段子字符串是否合法,如果合法就放进result中
            if (isValid(s, startIndex, s.size() - 1)) {
                result.push_back(s);
            }
            return;
        }
        for (int i = startIndex; i < s.size(); i++) {
            if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法
                s.insert(s.begin() + i + 1 , '.');  // 在i的后面插入一个逗点
                pointNum++;
                backtracking(s, i + 2, pointNum);   // 插入逗点之后下一个子串的起始位置为i+2
                pointNum--;                         // 回溯
                s.erase(s.begin() + i + 1);         // 回溯删掉逗点
            } else break; // 不合法,直接结束本层循环
        }
    }
    // 判断字符串s在左闭又闭区间[start, end]所组成的数字是否合法
    bool isValid(const string& s, int start, int end) {
        if (start > end) {
            return false;
        }
        if (s[start] == '0' && start != end) { // 0开头的数字不合法
                return false;
        }
        int num = 0;
        for (int i = start; i <= end; i++) {
            if (s[i] > '9' || s[i] < '0') { // 遇到非数字字符不合法
                return false;
            }
            num = num * 10 + (s[i] - '0');
            if (num > 255) { // 如果大于255了不合法
                return false;
            }
        }
        return true;
    }
public:
    vector<string> restoreIpAddresses(string s) {
        result.clear();
        if (s.size() < 4 || s.size() > 12) return result; // 算是剪枝了
        backtracking(s, 0, 0);
        return result;
    }
};

78.子集

题目链接
解题思路:
在这里插入图片描述从图中红线部分,可以看出 遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合。

回溯三部曲

  • 递归函数参数

全局变量数组path为子集收集元素,二维数组result存放子集组合。(也可以放到递归函数参数里)

递归函数参数在上面讲到了,需要startIndex。

代码如下:

vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex) {
  • 递归终止条件

从图中可以看出:

78.子集

剩余集合为空的时候,就是叶子节点。

那么什么时候剩余集合为空呢?

就是startIndex已经大于数组的长度了,就终止了,因为没有元素可取了,代码如下:

if (startIndex >= nums.size()) {
    return;
}

其实可以不需要加终止条件,因为startIndex >= nums.size(),本层for循环本来也结束了。

  • 单层搜索逻辑

求取子集问题,不需要任何剪枝!因为子集就是要遍历整棵树。

那么单层递归逻辑代码如下:

for (int i = startIndex; i < nums.size(); i++) {
    path.push_back(nums[i]);    // 子集收集元素
    backtracking(nums, i + 1);  // 注意从i+1开始,元素不重复取
    path.pop_back();            // 回溯
}

可以写出如下回溯算法C++代码:

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
        if (startIndex >= nums.size()) { // 终止条件可以不加
            return;
        }
        for (int i = startIndex; i < nums.size(); i++) {
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        result.clear();
        path.clear();
        backtracking(nums, 0);
        return result;
    }
};

90.子集II

题目链接
解题思路:
在这里插入图片描述代码如下:

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
        result.push_back(path);
        for (int i = startIndex; i < nums.size(); i++) {
            // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
            // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
            // 而我们要对同一树层使用过的元素进行跳过
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
            }
            path.push_back(nums[i]);
            used[i] = true;
            backtracking(nums, i + 1, used);
            used[i] = false;
            path.pop_back();
        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        sort(nums.begin(), nums.end()); // 去重需要排序
        backtracking(nums, 0, used);
        return result;
    }
};

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

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

相关文章

一种数据源切换的实践方案

随着业务的不断深入&#xff0c;我们会碰见很多关于数据源切换的业务场景&#xff0c;数据源切换也是当前最常用的分库后的分流策略方式之一&#xff0c;对于读写职责分离的数据库集群而言&#xff0c;我们在服务层面制定相应的接口与数据库交互的定制化开发&#xff0c;也就是…

云 cloud 高可用系统--在RDS上实现,从原理上不可能保证你100%不丢数据

我写这篇文字&#xff0c;实属无奈&#xff0c;在目前很多企业都依赖云的情况下&#xff0c;数据库的很多事情都是身不由己&#xff0c;发生问题&#xff0c;你查看日志&#xff0c;分析日志可能你连日志都不是全部的&#xff0c;并且想通过程序来过滤这个日志很多情况下都有限…

数据库系统概述——第六章 关系数据理论(知识点复习+练习题)

&#x1f31f;博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;离散数学考前复习&#xff08;知识点题&#xff09; &#x1f353;专栏&#xff1a;概率论期末速成&#xff08;一套卷&#xff09; &#x1f433;专栏&#xff1a;数字电路考前复习 &#x1f99a;专栏&am…

CMU 15-445 Project #2 - B+Tree(CHECKPOINT #1)

CHECKPOINT #1 一、题目链接二、准备工作三、部分实现1.查找操作2.插入操作3.删除操作 四、评测结果 一、题目链接 二、准备工作 见 CMU 15-445 Project #0 - C Primer 中的准备工作。 三、部分实现 对于B树的节点定义&#xff0c;通过节点类的命名 b_plus_tree_page 不难发现…

linux-centos7操作系统查看系统未挂载的磁盘,挂载磁盘

linux-centos7操作系统查看系统未挂载的磁盘,挂载磁盘 查看当前磁盘空间 根目录 / 下也只有44G,其他目录只有10几G,正式环境肯定不够用 df -h查看硬盘数量和分区情况 fdisk -l查看到/dev/vdb 有500多G了 将/dev/vdb在分出一个区使用 第一步:编辑分区。执行命令fdisk …

pr视频叠加,即原视频右上角添加另外一个视频方法,以及pr导出视频步骤

一、pr视频叠加&#xff0c;即原视频右上角添加另外一个视频方法 在使用pr制作视频时&#xff0c;我们希望在原视频的左上角或右上角同步播放另外一个视频&#xff0c;如下图所示&#xff1a; 具体方法为&#xff1a; 1、导入原视频&#xff0c;第一个放在v1位置&#xff0c;第…

Selenium编写自动化用例的8种技巧

在开始自动化时&#xff0c;您可能会遇到各种可能包含在自动化代码中的方法&#xff0c;技术&#xff0c;框架和工具。有时&#xff0c;与提供更好的灵活性或解决问题的更好方法相比&#xff0c;这种多功能性导致代码更加复杂。在编写自动化代码时&#xff0c;重要的是我们能够…

【序列dp】最长上升子序列(一)

文章目录 最长上升子序列-序列dp概览895 最长上升子序列-O(n^2)1017 怪盗基德的滑翔翼1014 登山482 合唱队形1012 友好城市 最长上升子序列-序列dp 什么是序列相关的 DP &#xff1f;序列相关 DP&#xff0c;顾名思义&#xff0c;就是将动态规划算法用于数组或者字符串上&…

textgen教程(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 官方GitHub项目&#xff1a;shibing624/textgen: TextGen: Implementation of Text Generation models, include LLaMA, BLOOM, GPT2, BART, T5, SongNet and so on. 文本生成模型&#xff0c;实现了包括LLaMA&#xff0c;ChatGLM&#xff0c;B…

C++课程学习记录

目录 1. 前置说明2. 二叉树的模拟2.1 参考资料2.2 二叉树的构建2.2.1 递归构建2.2.2 迭代构建 2.3 二叉树的遍历2.4 二叉树的应用 3. 继承与派生3.1 最简单的生死3.2 动态申请空间的生死3.3 继承中的protectd权限3.4 三种继承方式3.5 修改某些继承成员的继承类型3.6 多级派生3.…

C++57个入门知识点_番外1_C++指针偏移在类中的应用及指针偏移原理

这是对C指针偏移介绍比较好的博文&#xff0c;但是比较分散&#xff0c;我把其进行了整理&#xff0c;原博文地址请见最后&#xff0c;讲的很详细。 C57个入门知识点_番外1_C指针偏移在类中的应用及指针偏移原理 1. C指针偏移原理2. C显示十进制内存地址&#xff08;不用理解&…

AQS原理

目录 一、原理概述二、AQS 对资源的共享方式三、AQS底层使用了模板方法模式四、使用demo&#xff0c;使用AQS实现不可重入锁五、AQS使用到的几个框架 一、原理概述 AQS全称是 AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架 AQS核心思想是&#…

Appian低代码平台

国外老牌低代码开发平台Appian Appian在国内用的比较少&#xff0c;资料也很匮乏。需要自己主动去官网寻找。 Appian 学习平台 进入Appian Community可以选择学习路径&#xff0c;可以选择适合自己的学习路径&#xff1b;我选择的是Builder路径&#xff0c; 看了足足80个小…

opencv检测二维码和条形码

文章目录 1 excel制作简单二维码2 识别二维码和条形码2.1 相关库2.2 decode解码2.3 圈出二维码的位置2.4 判断二维码是否授权 3 完整代码3.1 使用图片进行识别3.2 使用摄像头实时识别 4 总结 1 excel制作简单二维码 使用excel可以实现制作二维码&#xff0c;但只能实现做英文和…

基于51单片机的简易电子琴设计

目录 摘 要 基于51单片机的简易电子琴设计 一、系统设计 1、项目概要 2.设计任务和基本要求 二、硬件设计 1、硬件设计概要 2、时钟振荡电路模块 3.复位电路模块 5.数码管电路模块 6.蜂鸣器模块 7、乐曲切换电路模块 三、软件原理 四、软件流程图 五、代码实现 …

解析Transformer基本结构与实现

1.基本结构 ​ Transformer总体架构可分为4个部分&#xff1a; 输入部分-输出部分-编码器部分-解码器部分 输入部分包含&#xff1a; 原文本嵌入层&#xff08;Input embedding&#xff09;及其位置编码(position encoding)目标文本嵌入层及其位置编码器 文本嵌入层的作…

TCP/UDP协议重温三次握手四次挥手 简单笔记

术语储备&#xff1a; SYN&#xff1a;同步位 &#xff1b;SYN1,表示进行一个连接请求 ACK&#xff1a;确认位 &#xff1b;ACK1,确认有效 ACK0&#xff0c;确认无效 ack : 确认号 &#xff1b;对方发送序号1 seq &#xff1a; 序号 ; 标识从TCP发端向TCP收端发送的数据字节流 …

基于JPA的Repository使用详解

Spring Data JPA Spring Data是Spring提供的操作数据的框架&#xff0c;Spring Data JPA是Spring Data的一个模块&#xff0c;通过Spring data 基于jpa标准操作数据的模块。 Spring Data的核心能力&#xff0c;就是基于JPA操作数据&#xff0c;并且可以简化操作持久层的代码。…

BLOND:ISH VoxEdit 创作大赛来啦!

准备好随着 BLOND:ISH 的节拍释放你们的创造力和节奏&#xff0c;因为我们将举办一场与众不同的刺激比赛。你们可以在 BLOND:ISH VoxEdit 大赛中展示你们的才华并赢得 SAND 奖励&#xff01; &#x1f3dd;️ 比赛主题&#xff1a;ABRA 夏日派对 &#x1f3dd;️ 释放你们的想象…

【Python学习】—Python基础语法(二)

文章目录 【Python学习】—Python基础语法&#xff08;二&#xff09;一、字面量二、注释三、变量四、数据类型五、数据类型转换六、标识符七、运算符八、字符串扩展九、获取键盘输入 【Python学习】—Python基础语法&#xff08;二&#xff09; 一、字面量 字面量&#xff1…