Learning C++ No.10【STL No.2】
引言:
北京时间:2023/2/14/23:18,放假两个月,没有锻炼,今天去跑了几圈,一个字,累,感觉人都要原地升天了,所以各位小伙伴,准确的说是各位卷王,一定要坚持锻炼,不敢像我一样哦!身体才是本钱,锻炼才是王道,并且今天看了一下小黑书和计算机导论中的一些有关计算机系统的知识,发现以前通过别人把饭嚼碎给我们吃获得知识的方式,在碰到一个很陌生的知识点的时候,只能通过强硬的记忆理解去理解这个知识点,导致我们不能很好的理解这个知识点,所以虽然通过人家把饭嚼碎给我们吃,这样能够更容易上道,但是会导致不易学透;所以当自己去看书的时候,去理解书中讲解的知识点的时候,我充分发现,还是一定要自己学会吃饭,不能一直吃别人嚼碎的饭,这样才可以更好的理解某些知识点;本今天是打算自己开一个专栏,记录一下今天看到的几个有关计算机系统的,比较容易理解不全面的,当然相对于我自己来说,是和自己以前的理解有所偏差的知识点,但是由于时间问题,所以今天并没有记录,明天咱们再搞这一块。现在让我们再次进入STL的世界,开始string类的二次深入学习吧!

再谈string类
string类其实就是一个管理字符串的类,里面的函数就是用来对字符进行增删查改用的,用来像顺序表一样,管理一个字符数组;在上篇博客中,我们已经把string类中的一些接口给学习过了,现在我们就继续学习一下别的string类中的接口,首先是接着上篇博客,学习一下有关容量的接口。
首先我们就先来看一下容量函数中的capacity接口,观察一下它是怎么进行扩容的,如图:


我们可以发现,在vs中capacity函数大致是以1.5倍进行扩容的,而在Linux中,capacity函数大致是以2倍进行扩容的,所以我们可以得出结论,在不同的编译器上,STL库是有一定的区别的,所以导致string类中的capacity函数是有一定的区别的,所以导致扩容的倍数是有一定区别的。
讲述上述capacity函数的扩容原理,本质上我们是想要为了解string类中的reverse函数做一些铺垫的,reserve函数的作用就是可以直接进行开空间,当知道需要多少空间时,可以直接提前开空间,把空间开好之后,就是可以减少扩容,因为扩容是有消耗的,所以可以提高程序的效率。前提是知道需要多少空间哦!如下图就是reverse函数开空间的使用方法:


可以看出在编译器不同的情况下,使用reserve函数直接开空间,也是存在着一定的差异的,但是一定要区分reverse函数(逆置函数)。搞定了reserve函数,此时我们再来看一看什么是resize函数,


如图我们可以发现resize函数有两种用法,一种是开辟空间,和更改size的大小,一种是可以给一个字符,把剩下的size空间全部填充该字符,如上图中所示,并且此时从另一个层面去看,resize函数还具有删除数据的功能(原理:如果此时的容量比resize开的小,那么就增大空间,如果比resize开的大,那么就减小空间),此时的减小空间就可以理解为此时的删除数据,例:如图

如图可以发现,此时的size确实是变成了5,然后只剩下了hello,把后面的内容都给删除了,也可以发现capacity容量是没有改变的。所以本质上resize就是在对string类中的成员变量中指针指向的那块空间的数组进行改变而已。
浅浅摸一下迭代器
我们可以知道迭代器是STL中的一个重点知识,所以此时我们就先来浅浅的摸一下它,为以后迭代器的学习打下一定的基础。
如图就是迭代去在string中使用的一个经典代码:
正序遍历
这个就是 string类 中迭代器的基本函数:

综合上述的两幅图,可以发现,begin就是迭代器的开始位置,end就是迭代器的结束位置,上述代码中,我们使用这两个迭代器函数就可以很好的遍历一个字符串,但是要注意,在使用begin和end的时候,我们最终得到的还是该位置的地址而已,所以想要获取其中的数据,就一定需要进行解引用操作,并且目前我们可以把迭代器想象成C语言中的指针,但是以后就不行了,因为后面的迭代器会更加的复杂,我们这里只是简单的摸一下它而已, 但是在底层的代码实现,本质上迭代器肯定还是使用指针实现。并且当我们学了迭代器,我们就可以使用三种不同的方式去访问string, 范围for、迭代器、下标+[] ,但是 下标+[]的本质上是使用了[]运算符重载 ,范围for本质上是使用了迭代器,足以看出迭代的重要性,STL六巨头之一不是徒有虚名的。
逆序遍历
并且可以看出,上述我们对数组的遍历是从前向后遍历的,所以该迭代器称之为正向迭代器,接下来我们就介绍一下什么是反向迭代器

注意: 因为此时是反向迭代器,所以rbegin是在rend的后面的,所以此时rbegin想要靠近rend也是要使用加加,不可以使用减减。
总:正向迭代器,加加表示向前走,反向迭代器,加加表示向后走。
了解了什么是正向迭代器,什么是反向迭代器,此时我们就根据迭代器的类型再来聊一聊,普通迭代器和const修饰的迭代器, 如下图:

此时我们可以发现,无论是正向迭代器,还是反向迭代器,它们都具有两种类型,一种是普通的iterator、reverse_iterator类型,另一种是const_iterator、const_reverse_iterator类型,此时的普通类型迭代器是允许遍历和读写容器的数据的,而const修饰的迭代器只允许遍历和读,并不允许写。
如下代码就是这4中类型的迭代器:
#include<iostream>
#include<string>
using namespace std;int main()
{string s1("hello world");string::iterator it = s1.begin();//正向迭代器while (it != s1.end()){cout << *it << " ";++it;}cout << endl;string::reverse_iterator rit = s1.rbegin();//反向迭代器while (rit != s1.rend()){cout << *rit << endl;++rit;}cout << endl;string::const_iterator it = s1.begin();//const正向迭代器while (rit != s1.rend()){cout << *it << endl;++rit;}cout << endl;string::const_reverse_iterator rit = s1.rbegin();//const反向迭代器while (rit != s1.rend()){cout << *rit << endl;++rit;}cout << endl;return 0;
}
并且此时可以发现,迭代器的类型的返回值类型是比较长的,所以此时我们就可以使用以前学的一个自动匹配类型的关键字auto ,使用auto的好处就凸显出来了,减少代码量,所以auto在有些场景中是非常的实用的,不过前提是我们自己知道这个函数的返回值类型具体是什么,这样使用auto才可以让我们更加遍历,但是auto也是有一定的缺点的,就是导致代码的可读性很低,只有了解这部分知识的人,才可以看懂代码,如下图:就是auto和迭代器的结合使用。

注意: 此时const类型的迭代器中的const修饰的是数据不能被修改,并不是位置不能被修改,所以此时rit是可以修改的,*rit是不可以修改的。
为什么要学习迭代器
很多同学可能会有疑问,我们可以使用下标和重载运算符[]来实现字符串的遍历,那么我们为什么还要学习迭代器呢?不都是为了遍历字符串吗?原因:链表和树状结构,如果使用**重载运算符operator[]**的效率是非常的低下,所以就不可以使用[]运算符和下标进行重载,此时就只可以只能使用迭代器,只有使用迭代器才可以让遍历其它非数组结构的数据结构的效率更高 ,所以C++中迭代器的概念是为了给除了数组以外的数据结构使用的。
总:迭代器作为STL中的六巨头之一,不是浪得虚名的,非常重要!
string类中的各种接口
string类中的insert函数
如图insert函数就是对字符串进行任意位置的插入字符或者字符串而已,并且也可以使用迭代器的形式就行任意位置的插入,并且insert函数的重载函数是非常多种的,可以说是满足任何的插入需求,但是注意: 因为insert每次插入字符都是以移动字符为前提的,所以在string类中,我们应该少使用insert函数,这样可以提高我们代码的效率。
string类中的erase函数
上述最后一个使用样例是属于迭代区间,目前我们就先不做了解,以后再了解,并且此时的erase函数也是和insert一样,我们并不支持经常使用,原理:经常挪动数据效率低下。
string类中的replace函数
该函数的使用是非常的不友好的,因为它不仅需要进行扩容,还需要进行字符的挪动,只有这样才可以实现该函数,所以效率是非常的低下的。
string类中的find函数
学习find函数,此时我们通过一个题目来搞定。
题目:把一句英文中的空格全部给替换成目标字符串。
代码如下:

我们使用了find函数的第一个样例,在特定的位置开始找目标字符,并且此时有一个缺省值(0),如果不是特定位置,它就会默认从0开始找目标字符,但是我们为了提高程序的效率,我们可以把这个值给成pos位置的后一个位置或者后几个位置,目的就是为了让find函数,不用每次都从头开始找,而是直接跳过我插入的特定字符,从特定字符的后一个位置去找,这样每次都是在向后遍历,极大的提高了程序的时间复杂度,并且如代码中,我们还使用了reserve函数进行开空间,前提是我们知道此时需要多少的空间,这样就可以避免扩容带来的消耗,极大的提高程序的效率。
并且此时该题还有第二种写法,一种用空间换时间的写法,但是代码的缺陷和上述代码还是差不多的,都是可以使用提前开空间(reserve)函数来进行优化的,如下代码:
#include<iostream>
#include<string>
using namespace std;int main()
{string str("hello world i love you");string newStr;size_t num = 0;for (auto ch : str){if (ch == ' '){++num;;}}newStr.reserve(str.size() + 2 * num);//还是提前把空间开好for (auto ch : str){if (ch != ' '){newStr += ch;}else{newStr += "20%";}}str = newStr;cout << str << endl;return 0;
}
从这个题目,我们可以发现,在string类中是有非常多好用的功能(通过各种函数接口),使我们做题变得更加的简洁和灵活,只要你把string类中的函数给熟练的使用,是真的可以很好的利用它们去解决各种有关字符的题目。
利用string类中的函数解决问题
搞定了find函数,我们把string类中的常见的函数就给搞定的差不多了,此时我们就可以使用这些函数来做一些题目了,如下题:
给你一段有运算符和英文字母的语句,然后仅反转其中的英文字母,非英文字母保留在原有位置,所有英文字母(小写或大写)位置反转,然后返回反转后的 s
例:
输入:s = “ab - cd”
输出:“dc - ba”
并且此时通过这个题目,有一个注意点,就是if和if的使用和if和else if的使用
| 1.从字面上理解if为如果,就是如果这种情况,如果那种情况。 |
|---|
| 2.else if 不是上一个条件的前提下,如果是这个条件。(总:else if是在上一个条件不成立的情况下,进行判断) |
| 3.区别1:if无论是否满足条件都会向下执行,直到程序结束,else if 满足一个条件就会停止执行。(总:else if 一但满足之后,就会停止程序) |
| 4.区别2:由于if都会执行一遍,则可能会同一个需要判断的事件,会进入2个if语句中,出现错误,而else if就不会发生这样的事情。 |
弄清楚了if、if使用和if、else if的使用,此时我们就正式进入题目:
思路:
- 当我们想要去遍历该语句的时候,思考使用那种遍历的方式最合适,发现范围for和迭代器不怎么合适,所以使用下标+[]
- 实现判断是否是英文字母的函数,并且注意:前后的开始位置
- 前后同时遍历,找字母,是字母就停下来,然后交换,不是字母就加加到后面一个,或者减减到前面一个
#include<iostream>
#include<string>
using namespace std;class Solution
{
public:bool isLetter(char ch){if (ch >= 'a' && ch <= 'z'){return true;}if (ch >= 'A' && ch <= 'Z'){return true;}else{return false;}}string reverseOnlyLetters(string s){size_t begin = 0;size_t end = s.size() - 1;while (begin < end){while (begin < end && !isLetter(s[begin])){++begin;}while (begin < end && !isLetter(s[end])){++end;}swap(s[begin], s[end]);begin++;end--;}return s;}
};
有了上述的思路和string类中的各种函数接口的使用,我们很愉快的就搞定了该题,所以下一题吧!!!
如题:
字符串中的第一个唯一字符,给定一个字符串s,找到它的第一个不重复字符,并返回它的索引,如果不存在,则返回-1(false)
例:
输入:s = “leetcode”
输出:1
原理:
- 该题第一时间,就可以想到使用计数排序的思想,使用映射的方式进行是最好的方法
- 按照计数排序的思想,此时第一个步骤就是统计每个字母出现的次数
- 判断谁是只出现一次,并且是第一个出现的(通过字符串和映射的数组中统计的次数,直接和1比较就行了)
#include<iostream>
#include<string>
using namespace std;class Solution
{
public:int firstUniqChar(string s){int countA[26] = { 0 };//将该数组初始化为0,方便映射,不需要我们自己去初始化了,以前计数排序的时候使用的是memset初始化for (auto ch : s){countA[ch - 'a']++;//按照原理:下标0放a,1放b,2放c,3放d,所以此时按照这个下标理论,就可以让ch中的某个字母-掉a的ASCII码值,这样就可以在相应的下标位置进行++了}for (int i = 0; i < s.size(); ++i)//此时这个遍历(因为要知道下标,所以不适合使用范围for和迭代器,使用下标是最合适的){//总:遍历,一定要灵活选择if (countA[s[i] - 'a'] == 1){return i;}}}
};
通过上题,我们可以发现计数排序的好处,但是也可以发现,我们有时候,很难去使用,特别是countA[ch - 'a']++; 统计每一个字符出现的次数的这步,有时候需要我们仔细斟酌,还有就是这步 if (countA[s[i] - 'a'] == 1),让字符串中的字符从前向后去和统计出的出现次数进行比较,看谁是第一个出现一次的,这步也是需要我们细细斟酌,所以这两步一定要熟练的使用,这样才可以让我们的代码更加的优,并且思路更加的清晰,做题更加的轻松。
总:计数排序的思想是非常的好用的,并且string的使用场景(遍历)大部分都是:下标+[]配合使用的。

总:搞定了string类中常用函数和如何使用,不仅可以为以后STL的学习提供便利,而且可以让我们做题变得更加的轻松。
相关文章:
Learning C++ No.10【STL No.2】
引言: 北京时间:2023/2/14/23:18,放假两个月,没有锻炼,今天去跑了几圈,一个字,累,感觉人都要原地升天了,所以各位小伙伴,准确的说是各位卷王,一定…...
【java 高并发编程之JUC】2w字带你JUC从入门到精通
点击查看脑图目录地址,实时更新 1 什么是 JUC 1.1 JUC 简介 在 Java 中,线程部分是一个重点,本篇文章说的 JUC 也是关于线程的。JUC 就是 java.util .concurrent 工具包的简称。这是一个处理线程的工具包,JDK 1.5 开始出现的。 1.2 进程与…...
QCon演讲实录(下):多云管理关键能力实现与解析-AppManager
在上篇中,我们已经基本了解了多云管理。现在,我们将深入探讨多云管理关键能力实现:AppManager。 什么是AppManager? 上面我们讲了理论、我们自己使用的交付流程和整体架构,下面我们进入关键能力实现与解析的环节&…...
刚刚退出了一个群,关于在要麒麟OS上运行Labview
年龄过了45,看问题,与以前不太一样了。 觉得浪费时间的事,宁可发呆,也不会参和。 竟然一个群里在讨论如何满足客户的需求:麒麟OS上运行Labview。 然后直接退了群。 这种问题,我觉得可能 发在csdn上&…...
el-uploader 文件上传后,又被修改,无法提交到后端 ERR_UPLOAD_FILE_CHANGED
problem 文件上传后,又被修改,无法提交到后端 具体步骤: 文件上传本地文件打开并修改保存提交ajax 这个问题不仅仅局限于el-uploader,是一个普遍性的问题 导致的问题 问题1:提交请求时,控制台报错 net…...
利用Eigen实现点云体素滤波
目录 前言 一、算法原理 二、代码实现 1.头文件 2.源文件 三、效果展示 前言 体素滤波原理简单,是常用的...
linux高级命令之多进程的使用
多进程的使用学习目标能够使用多进程完成多任务1 导入进程包#导入进程包import multiprocessing2. Process进程类的说明Process([group [, target [, name [, args [, kwargs]]]]])group:指定进程组,目前只能使用Nonetarget:执行的目标任务名…...
CSS 圆角边框 盒子阴影 文字阴影
目录 1.圆角边框(重点) 2.盒子阴影(box-shadow) 3.文字阴影(text-shadow) 1.圆角边框(重点) border-radius 属性用于设置元素的外边框圆角。 语法: border-radius: l…...
python简单解析打印onnx模型信息
当我们加载了一个ONNX之后,我们获得的就是一个ModelProto,它包含了一些版本信息,生产者信息和一个GraphProto。在GraphProto里面又包含了四个repeated数组,它们分别是node(NodeProto类型),input(ValueInfoProto类型)&a…...
UE4 编写着色器以及各种宏的理解
参考链接:如何为 UE4 添加全局着色器(Global Shaders) - Unreal Enginehttps://docs.unrealengine.com/5.1/zh-CN/adding-global-shaders-to-unreal-engine/如何为 UE4 添加全局着色器(Global Shaders) - Unreal Engin…...
小笔记:Python 使用字符串调用函数
小笔记:Python中如何使用字符串调用函数/方法?jcLee95:https://blog.csdn.net/qq_28550263?spm1001.2101.3001.5343 本文地址:https://blog.csdn.net/qq_28550263/article/details/111874476 邮箱 :291148484163.co…...
红黑树的原理+实现
文章目录红黑树定义性质红黑树的插入动态效果演示代码测试红黑树红黑树 定义 红黑树是一个近似平衡的搜索树,关于近似平衡主要体现在最长路径小于最短路径的两倍(我认为这是红黑树核心原则),为了达到这个原则,红黑树所…...
用于非线性时间序列预测的稀疏局部线性和邻域嵌入(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
使用 Vue3 重构 Vue2 项目
目录前言:一、项目整体效果展示二、项目下载使用方法三、为什么要重构项目四、重构的流程五、步骤中的 bug 以及解决方式六、未解决的问题总结:前言: 2020年9月18日,vue3正式版发布了,前几天学习完成后,我决…...
Hive学习——单机版Hive的安装
目录 一、基本概念 (一)什么是Hive (二)优势和特点 (三)Hive元数据管理 二、Hive环境搭建 1.自动安装脚本 2./opt/soft/hive312/conf目录下创建hive配置文件hive-site.xml 3.拷贝一个jar包到hive下面的lib目录下 4.删除hive的guava,拷贝hadoop下的guava 5…...
uprobe 实战
观测数据源 目前按照我的理解,和trace相关的常用数据源–探针 大致分为四类。 内核 Trace point kprobe 用户程序 USDT uprobe 在用户程序中,USDT是所谓的静态Tracepoint。和内核代码中的Trace point类似。实现方式是在代码开发时,使用USDT…...
华为OD机试 - 求最大数字(Python)| 真题+思路+考点+代码+岗位
求最大数字 题目 给定一个由纯数字组成以字符串表示的数值,现要求字符串中的每个数字最多只能出现2次,超过的需要进行删除;删除某个重复的数字后,其它数字相对位置保持不变。 如34533,数字3重复超过2次,需要删除其中一个3,删除第一个3后获得最大数值4533 请返回经过删…...
雨水情测报与大坝安全监测系统
压电式雨量传感器产品概述传感器由上盖、外壳和下盖组成,壳体内部有压电片和电路板,可以固定在外径50mm立柱上和气象站横杆上。传感器采用冲击测量原理对单个雨滴重量进行测算,进而计算降雨量。雨滴在降落过程中受到雨滴重量和空气阻力的作用…...
抖音广告投放形式有哪些?新品牌进入抖音怎么建立口碑
坐拥5亿用户的抖音平台,已经成为各大品牌的兵家必争之地。想要在这块宣传的“高地”,做出声量,就必须了解抖音广告投放形式有哪些。这里整理的这份抖音广告投放指南,你一定不能错过。一、抖音为何如此牛想要弄清楚抖音广告的投放形…...
Beefxss使用教程图文教程(超详细)
「作者主页」:士别三日wyx 「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 Beefxss一、首次使用二、修改账号密码三、自带练习页面四、简单使用五、工具界面介绍六、功能演示1、网页重定向2、社工弹窗3、功能颜色标识…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...
