回溯算法:复原IP地址 子集 子集II

news2025/1/12 1:41:36

93.复原IP地址 

  • 思路:
    • 与分割回文串相似,复原ip地址是将给定字符串分割成点分十进制的四段,切割问题就可以使用回溯搜索法把所有可能性搜出来。
    • 回溯三部曲:
      • 递归参数:除了传入的需要分割的字符串,仍然需要startIndex保证不会重复分割,还需要一个变量pointNum来记录已添加的逗点数量。
      • 终止条件:当分割出四段,也就是逗点数量记录为3时,说明分割结束。再验证最后一段的合法性,若合法就可以加入结果集中。前三段的合法性在添加逗点之前判断,因为最后一段之后没有逗点了,所以要单独判断。
      • 单层逻辑:在for循环中,判断截取的左闭右闭区间子串 [startIndex, i] 合法后,在后面一位加上‘ . ’表示已经分割,记录分隔符的变量pointNum加一,再从‘ . ’的下一位开始继续递归,回溯时将‘ . ’再删除,pointNum减一。如果不合法,结束本层循环。
    • 判断子串是否合法:
      • 以0开头,但不只有0一个字符的子串不合法
      • 含有非0-9字符的子串不合法
      • 大于255的不合法
    • 剪枝:输入长度不合法的字符串时(主函数中),子串不合法时剪枝(isValid)。
    • 时间复杂度: O(3^4),IP地址最多包含4个数字,每个数字最多有3种可能的分割方式,则搜索树的最大深度为4,每个节点最多有3个子节点。
    • 空间复杂度: O(n)
class Solution {
private:
    vector<string> res;
    //startIndex记录起始位置(切割线),pointNum记录已添加的逗点数量
    void backtracking(string& s, int startIndex, int pointNum) {
        if(pointNum == 3) {//如果已添加三个逗点,说明已经切割成四段,切割完毕
            //判断切割的最后一段的合法性,合法就收集结果
            if(isValid(s, startIndex, s.size() - 1)) {
                res.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, '.');//在这一字段后面添加逗点
                pointNum++;//逗点计数加一
                backtracking(s, i + 2, pointNum);//递归,从逗点后一位作为新的起始位置,所以是i+2
                pointNum--;//回溯
                s.erase(s.begin() + i + 1);//回溯
            } else break;
        }
    }

    //判断左闭右闭区间[start, end]字符串的合法性
    bool isValid(const string& s, int start, int end) {
        if(start > end) return false;
        //以0开头,但不只有0一个字符的区间不合法
        if(s[start] == '0' && start != end) 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) return false;//大于255则不合法
        }
        return true;
    }

public:
    vector<string> restoreIpAddresses(string s) {
        res.clear();
        if(s.size() < 4 || s.size() > 12) return res;//传入字符串的长度不合法
        backtracking(s, 0, 0);
        return res;
    }
};


78.子集

  • 思路:
    • 子集问题是找树的所有节点
    • 集合是无序的,子集{1,2} 和 子集{2,1}是一样的,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始。求排列问题的时候,就要从0开始,因为集合是有序的,{1, 2} 和{2, 1}是两个集合。
    • 回溯三部曲
      • 函数参数:除了输入的数组,还需要startIndex。
      • 终止条件:剩余集合为空的时候,就是叶子节点,此时startIndex已经大于数组的长度了,终止递归。其实可以不需要加终止条件,因为startIndex >= nums.size(),本层for循环本来也结束了,也不会导致无限递归
      • 单层逻辑求取子集问题,不需要任何剪枝,因为子集就是要遍历整棵树
    • 何时收集结果?
      • 我自己实现时的思路是每次path收集完一个元素,就收集一个结果,在主函数中单独收集空集。后面学习到把收集子集放在终止条件之前,就可以不用单独处理空集,也不会漏掉集合本身这个子集。
    • 时间复杂度: O(n * 2^n)
    • 空间复杂度: O(n)
class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
    void backtracking(vector<int>& nums, int startIndex) {
        res.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) {
        path.clear();
        res.clear();
        backtracking(nums, 0);
        return res;
    }
};


90.子集II

  • 思路:
    • 本题与78.子集的区别在于输入集合有重复元素,而要求输出的子集要去重,在40.组合总和II中,已经学习了如何处理这样的情况:首先要排序,为了让值相等的元素相邻,在横向遍历(for循环)时判断当前元素是否与前一个元素值相等,若相等,则跳过搜索

class Solution {
private:
    vector<int> path;
    vector<vector<int>> res;
    void backtracking(vector<int>& nums, int startIndex) {
        res.push_back(path);
        //if(startIndex >= nums.size()) return;
        for(int i = startIndex; i < nums.size(); i++) {
            //横向遍历时,值相同的两个元素不重复选取,实现去重
            if(i > startIndex && nums[i] == nums[i - 1]) continue;
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        path.clear();
        res.clear();
        sort(nums.begin(), nums.end());//排序方便去重
        backtracking(nums, 0);
        return res;
    }
};

总结

组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点

参考链接

代码随想录:复原IP地址   子集   子集II

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

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

相关文章

介绍几个有意思的 GitHub 仓库

大家好&#xff0c;我是风筝。 今天介绍几个很有意思的 github 开源项目&#xff0c;看过之后就会发现&#xff0c;github 果然深意暗藏。 GitHub对于程序员来说&#xff0c;再熟悉不过了&#xff0c;绝大多数时候&#xff0c;我们到上面都是为了学习高质量的源代码&#xff…

Windows循环检测,直到网络通/断后执行指定命令

前言 前几天&#xff0c;一个朋友让我帮他做个脚本或者批处理&#xff0c;要实现的功能很简单&#xff1a;开机时检测网络是否联通&#xff0c;如果联通了就执行一个指定的程序&#xff0c;然后脚本就可以退出了。 批处理的解决方法 手动操作时&#xff0c;我们通常使用ping…

【hacker送书第9期】算法训练营(入门篇)

第9期图书推荐 内容简介作者简介精彩书评图书目录概述参与方式 内容简介 本书以海量图解的形式&#xff0c;详细讲解常用的数据结构与算法&#xff0c;又融入大量的竞赛实例和解题技巧。通过对本书的学习&#xff0c;读者可掌握12种初级数据结构、15种常用STL函数、10种二叉树和…

HarmonyOS鸿蒙操作系统架构

目录 1. 分布式架构&#xff1a; 2. 统一的开发平台&#xff1a; 3. 多内核共享&#xff1a; 4. 自适应界面&#xff1a; 5. AR、VR、MR支持&#xff1a; 6. 安全和隐私保护&#xff1a; 7. AI集成&#xff1a; 8. 应用生态系统&#xff1a; 9. 开源和开放&#xff1a…

Maxwell学习笔记

1 概述 Maxwell 是由美国 Zendesk 开源&#xff0c;用 Java 编写的 MySQL 实时抓取软件。 实时读取MySQL 二进制日志 Binlog&#xff0c;并生成 JSON 格式的消息&#xff0c;作为生产者发送给 Kafka&#xff0c;Kinesis、RabbitMQ、Redis、Google Cloud Pub/Sub、文件或其它平台…

webpack学习-2.管理资源

webpack学习-2.管理资源 1.这章要干嘛2.加载css注意顺序&#xff01; 3.总结 1.这章要干嘛 管理资源&#xff0c;什么意思呢&#xff1f;管理什么资源&#xff1f;项目中经常会 导入各种各样的css文件&#xff0c;图片文件&#xff0c;字体文件&#xff0c;数据文件等等&#…

<Linux>(极简关键、省时省力)《Linux操作系统原理分析之文件管理(3)》(24)

《Linux操作系统原理分析之文件管理&#xff08;3&#xff09;》&#xff08;24&#xff09; 7 文件管理7.5 文件存储空间的管理7.6 文件的共享和保护7.6.1 文件存取控制7.6.2 文件共享的实现方法7.6.3 文件的备份转储 7 文件管理 7.5 文件存储空间的管理 位示图 对每个磁盘…

使用 PyTorch 完全分片数据并行技术加速大模型训练

本文&#xff0c;我们将了解如何基于 PyTorch 最新的 完全分片数据并行 (Fully Sharded Data Parallel&#xff0c;FSDP) 功能用 Accelerate 库来训练大模型。 动机 &#x1f917; 随着机器学习 (ML) 模型的规模、大小和参数量的不断增加&#xff0c;ML 从业者发现在自己的硬件…

harmony开发之image组件的使用

HarmonyOS是一款面向万物互联时代的、全新的分布式操作系统。 在传统的单设备系统能力基础上&#xff0c;HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念&#xff0c;能够支持手机、平板、智能穿戴、智慧屏、车机、PC、智能音箱、耳机、AR/VR眼镜等多种终端…

在KeyarchOS上体验WildFly

一、浪潮信息KeyarchOS简单介绍 KeyarchOS具备稳定可靠、高效软硬协同、全天候运维、安全可信等特性,增强了对云计算、人工智能等场景的支持,性能稳定性领先,生态完善,符合金融、政务、能源、交通、通信、教育、医疗等关键行业的应用要求。具备非常广泛的应用。 官方地址&…

BACnet I/O模块在水利环境监测全自动控制系统中的应用:稳定、高效、实时

前言 “绿水青山就是金山银山”&#xff0c;水利环境一直是国际生态部门关注的重点。随着经济的发展、针对水利环境的监管也日趋严格&#xff0c;尤其是重点河、湖水系、水源地、城市内河等成为重点监管对象&#xff0c;监管力度也愈来愈严格&#xff0c;监测布点密度不断加大。…

linux交换分区管理SWAP

6.2.5 交换分区管理SWAP 6.2.5.1 概念 作用&#xff1a; ”提升“内存容量&#xff0c;防止OOM&#xff08;out of memory&#xff0c;内存溢出&#xff09;。 ​ 对应windows中的虚拟内存。 ​ 从功能上讲&#xff0c;交换分区主要是在内存不够用的时候&#xff0c;将部分内…

【Azure 架构师学习笔记】- Azure Databricks (2) -集群

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (1) - 环境搭建 前言 在上文中提到了ADB 的其中一个核心就是集群&#xff0c;所以这里专门研究一下ADB 的集群。 ADB 集群 首先了解一下ADB…

git 使用记录

远程仓库为空初始化 初始化本地仓库 git init 在本地仓库书写代码&#xff08;这里可以编辑一个文本文件做测试&#xff0c;如hello.txt&#xff09; 5&#xff09;执行&#xff1a;git add 要让git管理的文件&#xff08;git add hello.txt&#xff09;>执行完此操作将我…

【Linux】Linux基础

文章目录 学习目标操作系统不同应用领域的主流操作系统虚拟机 Linux系统的发展史Linux内核版和发行版 Linux系统下的文件和目录结构单用户操作系统vs多用户操作系统Windows和Linux文件系统区别 Linux终端命令格式终端命令格式查阅命令帮助信息 常用命令显示文件和目录切换工作目…

数据湖和中央数据仓库的设计

设计数据湖或中央数据仓库是许多大型组织的主要职能&#xff0c;这些组织每天处理数百万笔交易&#xff0c;并对这些交易进行进一步的报告、预测或机器学习项目分析。 为了将所有来自源系统&#xff08;我们称之为“上游”&#xff09;到其他业务应用&#xff08;所谓“下游”&…

解决(error) ERR Errors trying to SHUTDOWN. Check logs.问题~

该问题出现在我在使用shutdown关闭redis服务器时&#xff0c;出现该问题的原因是由于配置文件的日志文件位置未配置或者缺少日志文件 我自己出现该问题是因为缺少日志文件&#xff0c;解决步骤如下所示&#xff1a; 第一步&#xff1a;在该目录下使用touch命令创建日志文件 第…

测绘资质测绘设备检定、校准管理制度

测绘设备检定、校准管理制度 建立健全测绘仪器设备检定、校准管理制度&#xff0c;明确测绘仪器设备的检定、校准、日常管理等要求

Kafka安装与配置-shell脚本一键安装配置(单机版)

文章目录 前言使用shell脚本一键安装1. 复制脚本2. 增加执行权限3. 执行脚本4. 加载用户环境变量5. 启动/停止Kafka内置zookeeper6. 启动/停止Kafka单机版 总结 前言 本文介绍了使用Shell脚本一键安装Kafka的方法。通过复制脚本并执行&#xff0c;可以自动下载、安装和配置Kaf…

【链表Linked List】力扣-24 两两交换链表中的节点

目录 题目描述 解题过程 题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;he…