代码随想录算法训练营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://xiahunao.cn/news/2979453.html

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

相关文章

Scala 05 —— 函数式编程底层逻辑

Scala 05 —— 函数式编程底层逻辑 该文章来自2023/1/14的清华大学交叉信息学院助理教授——袁洋演讲。 文章目录 Scala 05 —— 函数式编程底层逻辑函数式编程假如...副作用是必须的&#xff1f;函数的定义函数是数据的函数&#xff0c;不是数字的函数如何把业务逻辑做成纯函…

电子邮箱是什么?电子邮箱怎么申请注册?

虽然通过电子邮箱收发邮件办公已经成为常态&#xff0c;但是很多人不清楚电子邮箱是什么&#xff1f;电子邮箱是指通过网络传递的“邮局”&#xff0c;可以用来收发电子邮件。每个人的电子邮箱地址都是唯一的&#xff0c;确保他人的邮件能准确送到我们的电子邮箱之中。电子邮箱…

基于表面法线法的二维人脸图构建三维人脸模型matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ............................................................................for j 1 : …

二叉树之AVL树

文章目录 1. AVL树的概念&#xff08;logN)1.1背景1.2规则 2.AVL树节点的定义3.AVL树的插入4. AVL树的旋转(重点&#xff09;4.1 新节点插入较高的右子树的右侧&#xff1a;左单璇&#xff1b;4.2 新节点插入较高左子树的左侧&#xff1a;右单璇&#xff1b;4.3&#xff08;双旋…

wordpress建网站主题案例推荐

wordpress企业网站主题案例 https://www.mymoban.com/wordpress/ wordpress公司官网主题案例 https://www.wowsoho.com/jianzhan wordpress外贸主题案例 https://www.wpniu.com/moban

PromQL分组计数

count by(namespace) (kube_pod_info)计算指标kube_pod_info中每个namespace出现了几次。 结果如下&#xff1a;

ONLYOFFICE协作空间:团队高效协作的终极武器!

文章目录 ONLYOFFICE协作空间初创版专业版&#xff08;云端&#xff09;企业版&#xff08;内部部署&#xff09; 亮点功能实时多人协作编辑高效的项目管理工具无缝集成第三方存储服务安全性和合规性支持Markdown文件群组功能和存储配额管理嵌入功能和数据导入自托管协作空间支…

qdbus

qdbus 一些简单的使用 qdbus是对dbus的进一步封装&#xff0c;dbus是基于c实现的&#xff0c;在这里不做过多介绍&#xff0c;一些基本的概念可以参考以下链接 IPC之十一&#xff1a;使用D-Bus实现客户端向服务端请求服务的实例 QtDBus快速入门 一些简单的使用 qdbus 服务&am…

加密、解密、签名、验签、数字证书、CA浅析

一、加密和解密 加密和解密应用的很广&#xff0c;主要作用就是防止数据或者明文被泄露。 加解密算法主要有两大类&#xff0c;对称加密和非对称加密。对称加密就是加密和解密的密钥都是一个&#xff0c;典型的有AES算法。非对称加密就是有公钥和私钥&#xff0c;公钥可以发布…

基于JAVA高考志愿辅助填报系统

当今社会已经步入了科学技术进步和经济社会快速发展的新时期&#xff0c;国际信息和学术交流也不断加强&#xff0c;计算机技术对经济社会发展和人民生活改善的影响也日益突出&#xff0c;人类的生存和思考方式也产生了变化。传统高考志愿辅助填报采取了人工的管理方法&#xf…

AIGC时代之 - 怎样更好的利用AI助手 - 指令工程

爆火的AIGC 2022年11月30日&#xff0c;OpenAI发布ChatGPT 3 2022年12月4 日&#xff0c;ChatGPT 3 已拥有超过一百万用户 2023年各种大语言模型开始火爆全球 GPT们&#xff0c;已经成为了我工作和学习的非常重要的工具。 ChatGPT也没那么神奇&#xff1f; 不知道大家有没有…

数据通信核心

一.认识网络设备 互联网网络设备有AC,AP,防火墙,路由器&#xff0c;交换机等。 这里我们一起了解一下 框式交换机—— 主控板相当于大脑&#xff0c;属于控制平面 交换机网板——数据平面&#xff0c;转发平面——进行不同网卡之间的数据交换&#xff08;设备内部之间的转发…

达梦(DM)数据库表索引

达梦DM数据库表索引 表索引索引准则其他准则 创建索引显式地创建索引其他创建索引语句 使用索引重建索引删除索引 表索引 达梦数据库表索引相关内容比较多&#xff0c;常用的可能也就固定的一些&#xff0c;这里主要说一下常用的索引&#xff0c;从物理存储角度进行分类&#…

js生成不同的阅读数分配到每一篇上面,不会因为刷新而变动

js生成不同的阅读数分配到每一篇上面,不会因为刷新而变动 {%- for article in blog.articles -%}<div class"blog-articles__article article">{%- render article-card,article: article,media_height: section.settings.image_height,media_aspect_ratio: a…

揭开ChatGPT面纱(1):准备工作(搭建开发环境运行OpenAI Demo)

文章目录 序言&#xff1a;探索人工智能的新篇章一、搭建开发环境二、编写并运行demo1.代码2.解析3.执行结果 本博客的gitlab仓库&#xff1a;地址&#xff0c;本博客对应01文件夹。 序言&#xff1a;探索人工智能的新篇章 随着人工智能技术的飞速发展&#xff0c;ChatGPT作为…

53、图论-课程表

思路&#xff1a; 其实就是图的拓扑排序&#xff0c;我们可以构建一个图形结构&#xff0c;比如[0,1]表示1->0&#xff0c;对于0来说入度为1。 遍历结束后&#xff0c;从入度为0的开始遍历。引文只有入度为0的节点没有先决条件。然后依次减少1。直到所有节点入度都为0.然后…

Python语言第三章之容器类型(list, tuple)

高级数据类型 Python中的数据类型可以分为&#xff1a;数字型&#xff08;基本数据类型&#xff09;和非数字型&#xff08;高级数据类型&#xff09; 数字型包含&#xff1a;整型int、浮点型float、布尔型bool、复数型complex非数字型包含&#xff1a;字符串str、列表list、…

在线测径仪的六类测头组合形式!哪种适合你?

在线测径仪&#xff0c;这一现代工业的精密仪器&#xff0c;犹如一位技艺高超的工匠&#xff0c;以其卓越的性能和精准度&#xff0c;为工业生产提供了坚实的保障。它的出现&#xff0c;不仅提高了生产效率&#xff0c;更保证了产品质量&#xff0c;为企业的可持续发展注入了强…

360在线翻译免费API

一、需求&#xff1a; 根据360在线翻译&#xff0c;获取免费API&#xff0c;并调用 二、主要步骤 1、请求 url url "https://fanyi.so.com/index/search" 2、传入信息 datas {"query": "桌子"} 3、请求头 headers {"pro": &…