代码随想录算法训练营DAY28|C++回溯算法Part.4|93.复原IP地址、78.子集、90.子集II

文章目录

  • 93.复原IP地址
    • 思路
      • 确定非法的范围
      • 树形结构
    • 伪代码
  • 78.子集
    • 思路
    • 伪代码实现
    • CPP代码
  • 90.子集II
    • 思路
    • CPP代码
      • 用used去重的办法
      • 用set去重的版本
      • 不使用used数组、set的版本

93.复原IP地址

力扣题目链接

文章讲解:93.复原IP地址

视频讲解:回溯算法如何分割字符串并判断是合法IP?| LeetCode:93.复原IP地址

状态:一种十分典型的分割问题,根据IP地址的特征从分割一个字符、两个字符、三个字符推进,但是其中肯定是要进行剪枝的。代码的实现应该是什么样的呢?

思路

确定非法的范围

  1. 字符中出现非法字符串;
  2. 数字前面不允许有0:“1.013.1.1”;
  3. 不允许大于255。

这种分割问题,我们一定要用暴力枚举每一种分割的情况。

树形结构

树形结构中仍然还有许多细节需要注意:切割线如何固定在那里?然后剩余的切割线还能到后面继续切割?

如何去表达切割子串呢?

在这里插入图片描述

伪代码

  • 递归的返回值和参数:

    • 首先我们必须要有一个全局变量来记录结果vector<string> result
    • 其次,我们需要定义指示分割线的变量和我们添加逗点的数量
    • pointNum:我们的结果一定是要有逗点的,所以树的深度只有三层(不算根)
    vector<string> result;
    void backtracking(string& s, int startIndex, int pointNum){
    }
    
  • 递归终止条件:本题题目明确要求只分四段,也就如上文所说,3个pointNum就说明字符串分成四段了,我们检查一下第四段是否合法,就可以放入到结果集中了。

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

    • 本题中最重要的逻辑我觉得是没分割一次,检查一次子串合法性,如何加上逗点,不合法直接剪掉分支

    • 然后就是递归和回溯的过程:

      • 递归调用时,下一层递归的startIndex从i + 2开始,因为我们在字符串里加了逗点。同时记录分割符的数量pointNum + 1
      • 回溯时,记得删除逗点,然后pointNum也要-1
      for (int i = startIndex; i < s.size(); i++){if (isValid(s, startIndex, i)){s.insert(s.begin() + i + 1, '.'); //在i的后面插入一个逗点pointNum++;backtracking(s, i+ 2, pointNum);pointNum--;s.erase(s.begin() + i + 1);				// 回溯删掉逗点}else break;
      }
      
  • 判断子串是否合法

    • 段位以0开头的数字不合法
    • 段位里有非正整数字符不合法
    • 段位如果大于255不合法
    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)	return false;	//如果大于255}return true;
    }
    

78.子集

力扣题目链接

文章讲解:78.子集问题

视频讲解:回溯算法解决子集问题,树上节点都是目标集和! | LeetCode:78.子集

状态:第一次碰到子集 问题毫无思路,但是看完卡哥的树形结构和讲解,真的是豁然开朗

思路

啥也不说,先上树形结构图
在这里插入图片描述

从图中可以很明显得看出:组合问题和切割问题都是收集树的叶子结点,而自己问题是找树的所有结点

但是究其本质,子集问题仍然是一个组合问题,因为它的集合是无序的,子集{1, 2}{2, 1}是一样的。

所以既然是无序,取过的元素不会重复取,那么我们写回溯算法,for循环仍然要从startIndex开始而不是从0开始(后序的排列问题我们for就得从0开始啦,因为{1, 2}和{2, 1}是两个集合)

伪代码实现

  • 递归函数参数

    • 全局变量path和result
    • startIndex
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
    }
    
  • 递归终止条件:从树形结构可以看出,剩余集合为空的时候,我们的递归就结束了。startIndex已经大于数组的长度了,就终止了,因为没有元素可取了

if (startIndex >= nums.size()) {return;
}
  • 单层递归逻辑:子集问题不需要任何剪枝,因为子集就是要遍历整颗树!
for (int i = startIndex; i < nums.size(); i++) {path.push_back(nums[i]);    // 子集收集元素backtracking(nums, i + 1);  // 注意从i+1开始,元素不重复取path.pop_back();            // 回溯
}

CPP代码

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

力扣题目链接

文章讲解:90.子集II

视频讲解:回溯算法解决子集问题,如何去重?| LeetCode:90.子集II

状态:这个问题就是一个:去重。在之前的40.组合总和II中我们已经讨论过回溯算法中的去重问题。

思路

在这里插入图片描述

去重思路和40.组合总和II一致,这里直接给出代码

CPP代码

用used去重的办法

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;}
};

用set去重的版本

class Solution {
private:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, int startIndex) {result.push_back(path);unordered_set<int> uset;for (int i = startIndex; i < nums.size(); i++) {if (uset.find(nums[i]) != uset.end()) {continue;}uset.insert(nums[i]);path.push_back(nums[i]);backtracking(nums, i + 1);path.pop_back();}}public:vector<vector<int>> subsetsWithDup(vector<int>& nums) {result.clear();path.clear();sort(nums.begin(), nums.end()); // 去重需要排序backtracking(nums, 0);return result;}
};

不使用used数组、set的版本

本题也可以不使用used数组来去重,因为递归的时候下一个startIndex是i+1而不是0。

如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。

class Solution {
private:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, int startIndex) {result.push_back(path);for (int i = startIndex; i < nums.size(); i++) {// 而我们要对同一树层使用过的元素进行跳过if (i > startIndex && nums[i] == nums[i - 1] ) { // 注意这里使用i > startIndexcontinue;}path.push_back(nums[i]);backtracking(nums, i + 1);path.pop_back();}}public:vector<vector<int>> subsetsWithDup(vector<int>& nums) {result.clear();path.clear();sort(nums.begin(), nums.end()); // 去重需要排序backtracking(nums, 0);return result;}
};

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

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

相关文章

docker容器技术篇:安装与配置flannel

Docker安装与配置flannel flannel是什么&#xff1f; Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务&#xff0c;简单来说&#xff0c;它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址&#xff1b;通俗的将就是通过flannel接管dock…

粤嵌—2024/4/22—两数之和 || - 输入有序数组

代码实现&#xff1a; 双指针 /*** Note: The returned array must be malloced, assume caller calls free().*/ int* twoSum(int *numbers, int numbersSize, int target, int *returnSize) {int *res malloc(sizeof(int) * 2);*returnSize 2;int i 0, j numbersSize - 1…

C++学习进阶版(二):与文件相关的函数用法

目录 1、读取文件的指定行 &#xff08;1&#xff09;main函数中直接读 &#xff08;2&#xff09;封装成函数 ① 无返回值类型 ② 直接返回读取的内容 2、求文件的行数 3、文件内容读取成一个字符串 1、读取文件的指定行 &#xff08;1&#xff09;main函数中直接读 …

如何选择全链路监控系统?CAT、SkyWalking、Pinpoint哪个更适合?

如果服务器上没有应用还会造成硬件瓶颈吗&#xff1f;显然是不会的&#xff0c;呈现出来的硬件瓶颈绝大多数是表象问题&#xff0c;我们往往需要在系统应用上寻找问题的根因。而寻找系统问题的根因&#xff0c;对于系统链路监控也是必不可少的&#xff0c; 前面我们也写了几篇…

光伏无人机吊装技术的优势及应用前景

近年来随着政府对光伏业务的不断宣传和推进&#xff0c;不少区域因地制宜引进了光伏发电项目&#xff0c;但是部分区域由于交通不便利&#xff0c;给材料运输工作带来了巨大的考验。目前&#xff0c;无人机吊装技术运输材料解决这一问题。 今天带大家看下光伏无人机吊装技术有…

STM32G030F6P6TR ST意法

STM32G030F6P6TR是ST(意法半导体)一款基于高性能ArmCortex-M032位RISC内核&#xff0c;工作频率高达64MHz的32位MCU微控制器。代理销售ST(意法半导体)全系列IC电子元器件-中芯巨能为您提供STM32G030F6P6TR(ST 32位MCU)引脚图及中文参数介绍等内容。 STM32G030F6P6TR的中文参数 …

Linux进程和任务管理

目录 一.程序和进程的关系 程序 进程 线程 线程与进程的区别 二.查看进程信息ps 方法一 常用选项 方法二 三.TOP动态查看进程信息 进程信息区各列解释 top常用命令 系统查看命令总结 查看进程信息pgrep 查看进程树pstree 四.控制进程 进程的启动方式 进程的前…

Java面试八股之重写一个对象的equals方法,需要考虑哪些问题

重写一个对象的equals方法&#xff0c;需要考虑哪些问题 遵守equals()约定&#xff1a; 重写equals()方法应遵循Object类中定义的equals()方法约定&#xff0c;即实现自反性&#xff08;x.equals(x)始终为true&#xff09;、对称性&#xff08;若x.equals(y)为true&#xff0…

【电控笔记5.5】psms规格参数

规格参数 转矩常数Kt与反电动势常数Ke Kt:没安培电流产生多少转矩量 Ke或Kb:每单位转速产生的反电动势电压 反电动势是梯形波,Kt=Ke;正弦波则不相等 q轴电流与反电动势都领先转子磁通链90

谷粒商城学习笔记

1.系统架构 2.环境准备 21.安装Linux 1.VirtualBox: https://download.virtualbox.org/virtualbox/6.0.10/VirtualBox-6.0.10-132072-Win.exe 2.安装 Vagrant 1).Vagrant 下载地址: https://releases.hashicorp.com/vagrant/2.2.5/vagrant_2.2.5_x86_64.msi https://www…

7.Prism框架之对话框服务

文章目录 一. 目标二. 技能介绍① 什么是Dialog?② Prism中Dialog的实现方式③ Dialog使用案例一 (修改器)④ Dialog使用案例2(异常显示窗口) 一. 目标 1. 什么是Dialog?2. 传统的Dialog如何实现?3. Prism中Dialog实现方式4. 使用Dialog实现一个异常信息弹出框 二. 技能介…

Proxyman Premium for Mac:网络调试利器,开发者首选!

Proxyman Premium for Mac是一款功能强大的网络调试和分析工具&#xff0c;专为开发者和测试人员打造。这款软件以其出色的性能和丰富的功能&#xff0c;帮助用户在网络开发和调试过程中更有效地分析和拦截网络请求&#xff0c;进行必要的修改和重发&#xff0c;从而进行更深度…

【Linux基础】Linux基础概念

目录 前言 浅谈什么是文件&#xff1f; Linux下目录结构的认识及路径 目录结构 路径 家目录 什么是递归式的删除 重定向 输出重定向&#xff1a; 追加重定向&#xff1a; 输入重定向&#xff1a; 命令行管道 shell外壳 为什么需要shell外壳&#xff1f; shell外壳…

使用FPGA实现超前进位加法器

介绍 前面已经向大家介绍过8位逐位进位加法器了&#xff0c;今天向大家介绍4位超前进位加法器。 对于逐位进位加法器来说&#xff0c;计算任意一位的加法运算时&#xff0c;必须等到低位的加法运算结束送来进位才能运行。这种加法器结构简单&#xff0c;但是运算慢。 对于超…

NFT卡牌质押分红模式开发技术讲解分析

近年来&#xff0c;随着加密货币市场的快速发展&#xff0c;NFT&#xff08;Non-Fungible Token&#xff0c;非同质化代币&#xff09;作为一种独特的数字资产形式备受关注。其中&#xff0c;NFT卡牌质押分红模式是一种创新的应用场景&#xff0c;为用户提供了一种参与和投资的…

【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Check Box的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 按钮类控件 | Check Box的使用及说明 文章编号&#xff…

arduino使用光电开关控制电动平台回零

1.接线介绍 注意&#xff1a;NPN和PNP型光电开关的接线方式不同 首先&#xff0c;得在arduino上设置一个数字量接口&#xff0c;例如&#xff1a; #define PHOTO_SWITCH_PIN 53 // 假设光电开关连接到数字引脚53 然后根据你的情况把白色的或者黑色的线接到该引脚上&#x…

PCIe系统阻抗控制85还是100的验证

高速先生成员--周伟 还记得上次的文章&#xff0c;PCIe阻抗控制&#xff0c;85ohm和100ohm哪个好&#xff0c;文章里面只讲到目前的主要问题&#xff0c;但没有给出具体怎么解决这个问题&#xff0c;今天我们就通过无源仿真的方式来聊聊上次那个问题的最终解决方案。 目前我们看…

千锤百炼算法系列之动态规划

题外话 这段时间,我必须把算法弄明白 这篇直接讲解动态规划所有细节! 前面那篇 千锤百炼之每日算法(一)-CSDN博客 也有关于动态规划的讲解,也非常详细 很简单,我成尊不就是了?!!! 正题 动态规划 这里我们主要是让大家明白什么是动态规划,怎么用动态规划解题 我就不用…

IEC104协议

1. 简介 IEC104规约是一个广泛应用于电力、城市轨道交通等行业的国际标准。 2. 术语解释 遥脉 (电度量)&#xff1a; 是指对现场某装置所发出的脉冲信号进行周期累计的一种远程计数操作。 其实&#xff0c;遥脉也可以看成是被具体规定了采用脉冲计数作为测量方法的一种遥测…