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

news2024/11/25 23:17:41

文章目录

  • 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 > startIndex
                continue;
            }
            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.coloradmin.cn/o/1607073.html

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

相关文章

Hadoop数据压缩

Hadoop数据压缩 Hadoop 数据压缩是一种用于减少存储空间和网络传输成本的技术&#xff0c;通常应用于大数据处理场景。随着数据量的不断增长&#xff0c;对存储和网络带宽的需求也在增加&#xff0c;因此采用数据压缩技术可以有效地减少数据的存储和传输成本&#xff0c;提高数…

ERROR in [eslint] reorder to top import/first

情景再现&#xff1a;在react开发的时候&#xff0c;导入组件、函数时报错&#xff1a;Import in body of module; reorder to top import/first … 原因&#xff1a;在import语句前有变量声明 解决&#xff1a; 变量的声明&#xff0c;要放在import之后 // 错误示例 import {…

Transformer 代码详解(Pytorch版)

前言 基于上一篇经典网络架构学习-Transformer的学习&#xff0c;今天我们来使用pytorch 搭建自己的transformer模型&#xff0c;加深对transformer的理解&#xff0c;不仅在NLP领域绕不开transformer&#xff0c;而且在CV领域也是很火热&#xff0c;很多模型都用到了注意力机…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之三 简单人脸眼睛检测添加睫毛效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之三 简单人脸眼睛检测添加睫毛效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之三 简单人脸眼睛检测添加睫毛效果 一、简单介绍 二、简单人脸眼睛检测添加…

手机照片误删怎么恢复?别慌,教你轻松恢复!

如今数字化的时代&#xff0c;人们已经离不开手机&#xff0c;手机里珍藏的照片也成为了人与人之间的羁绊。然而有时候我们会因为手机故障或者操作失误&#xff0c;将手机照片误删。那么手机照片误删怎么恢复呢&#xff1f;三招教你找回误删的手机照片&#xff0c;有效实用且方…

激活函数:GELU(Gaussian Error Linear Units)

激活函数&#xff1a;GELU&#xff08;Gaussian Error Linear Units&#xff09; 前言相关介绍GELU&#xff08;Gaussian Error Linear Units&#xff09;代码示例 参考 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff0c;可点…

LUA脚本判断是否为空

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Lua是一个小巧的脚…

linux 的Jdk1.8详细安装部署教程

一、环境准备 1.下载安装包 下载地址&#xff0c;这是1.8的你也可以选择安装别的版本的 https://www.oracle.com/java/technologies/downloads/#java8-windows 选择想要的系统和对应的位数&#xff0c;点击下载即可 2.上传安装包 选择自己要安装的路径&#xff0c;&#x…

分步搭建HF区块链网络

一.搭建网络规划 采用容器环境&#xff0c;搭建1个排序节点(Orderer)、2个对等节点(Peer)&#xff0c;另外用 一个fabric-cli容器。实训中的绝大部分命令是通过该容器执行的。 容器名称设置 二. 配置HF网络证书 首先docker ps 检查镜像&#xff0c;确保镜像为空 1.生成crypto…

TDesign:腾讯的企业级前端框架,对标elementUI和ant-design

elementUI和ant-design在前端开发者中有了很高知名度了&#xff0c;组件和资源十分丰富了。本文介绍腾讯的一款B端框架&#xff1a;TDesign TDesign 是腾讯公司内部推出的企业级设计体系&#xff0c;旨在为腾讯旗下的各种产品提供一致、高效、优质的设计支持。这个设计体系是由…

今天给大家推荐36套404页面模板

404页面是网站必备的一个页面&#xff0c;它承载着用户体验与SEO优化的重任。当用户访问不存在的页面时&#xff0c;服务器会返回404错误代码&#xff0c;并显示404页面。一个好的404页面可以帮助用户快速找到所需信息&#xff0c;并提升网站的用户体验。 以下是一些演示下载资…

【每日一题】2007. 从双倍数组中还原原数组-2024.4.18

题目&#xff1a; 2007. 从双倍数组中还原原数组 一个整数数组 original 可以转变成一个 双倍 数组 changed &#xff0c;转变方式为将 original 中每个元素 值乘以 2 加入数组中&#xff0c;然后将所有元素 随机打乱 。 给你一个数组 changed &#xff0c;如果 change 是 双…

【ES】springboot集成ES

1. 去Spring官方文档确认版本兼容性 这一版的文档里没有给出springboot的版本对应&#xff0c;但我在一个博主的文章里看到的es8.0以前的官方文档中就有给出来&#xff0c;所以还需要再去寻找spring framework和springboot的对应关系&#xff1f;&#xff1f;&#xff1f; 还…

Java项目 苍穹外卖 黑马程序员

目录 day1一、项目效果展示二、项目开发整体介绍三、项目介绍3.1 定位3.2 功能架构3.3 产品原型3.4 技术选型 四、开发环境搭建4.1 前端环境4.2 后端环境 五、导入接口文档六、Swagger6.1 介绍6.2 使用方式6.3 常用注解 day2一、新增员工二、员工分页查询三、启用禁用员工账号四…

深度解析:基于隐马尔科夫模型的语音转文字技术

引言 语音转文字(Speech-to-Text, STT)技术作为人机交互的重要一环,在智能助手、自动字幕生成、语音命令识别等领域发挥着越来越重要的作用。隐马尔科夫模型(Hidden Markov Model, HMM)作为一种统计模型,因其在处理时间序列数据方面的优势,被广泛应用于语音识别系统中。…

蓝牙耳机推荐高性价比2024,运用六大实战技巧,精准捕获超值机型

​无论是通勤路上、健身房里&#xff0c;还是在家中放松时&#xff0c;蓝牙耳机都是我们享受音乐、提升生活品质的好帮手。随着蓝牙耳机技术的快速发展&#xff0c;各种款式层出不穷&#xff0c;选择起来确实不易。作为一名耳机发烧友&#xff0c;我认为是时候向大家推荐几款质…

Springboot+Vue项目-基于Java+MySQL的房屋租赁系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

Hive 解决数据倾斜方法

数据倾斜问题&#xff0c; 通常是指参与计算的数据分布不均&#xff0c; 即某个 key 或者某些 key 的数据量远超其他 key&#xff0c; 导致在 shuffle 阶段&#xff0c; 大量相同 key 的数据被发往同一个 Reduce&#xff0c; 进而导致该 Reduce 所需的时间远超其他 Reduce&…

深入探索生产者拦截器的使用以及源码设计

文章目录 一、介绍二、使用1. ProducerInterceptor接口2. 实现之统计3. 实现之二次处理4. 小结 三、实现原理1. 初始化流程2. 生效流程 四、总结 一、介绍 在软件设计中&#xff0c;为了方便能够应对不同的场景&#xff0c;一般在一些容易有差异的环节会考虑允许用户自定义逻辑…

数据结构(图)

定义 G (V, E) 图 (点&#xff0c;边) 图&#xff0c;Graph 点&#xff0c;Vertex 边&#xff0c;edge 有空表&#xff0c;空树&#xff0c;但没有空图 图可以没有边|E| 0&#xff0c;但不能没有一个点 稠密图 &稀疏图 是边的多少决定的 &#xff08;见Ex…