网站建设的作用和用途,企业诚信建设网站,织梦php网站,工业产品设计要学什么引言#xff1a;
北京时间#xff1a;2023/2/14/23:18#xff0c;放假两个月#xff0c;没有锻炼#xff0c;今天去跑了几圈#xff0c;一个字#xff0c;累#xff0c;感觉人都要原地升天了#xff0c;所以各位小伙伴#xff0c;准确的说是各位卷王#xff0c;一定…引言
北京时间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中类型的迭代器
#includeiostream
#includestring
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)函数来进行优化的如下代码
#includeiostream
#includestring
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.区别1if无论是否满足条件都会向下执行直到程序结束else if 满足一个条件就会停止执行。总:else if 一但满足之后就会停止程序4.区别2由于if都会执行一遍则可能会同一个需要判断的事件会进入2个if语句中出现错误而else if就不会发生这样的事情。
弄清楚了if、if使用和if、else if的使用此时我们就正式进入题目: 思路
当我们想要去遍历该语句的时候思考使用那种遍历的方式最合适发现范围for和迭代器不怎么合适所以使用下标[]实现判断是否是英文字母的函数并且注意前后的开始位置前后同时遍历找字母是字母就停下来然后交换不是字母就加加到后面一个或者减减到前面一个
#includeiostream
#includestring
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找到它的第一个不重复字符并返回它的索引如果不存在则返回-1false 例 输入s “leetcode” 输出1 原理
该题第一时间就可以想到使用计数排序的思想使用映射的方式进行是最好的方法按照计数排序的思想此时第一个步骤就是统计每个字母出现的次数判断谁是只出现一次并且是第一个出现的通过字符串和映射的数组中统计的次数直接和1比较就行了
#includeiostream
#includestring
using namespace std;class Solution
{
public:int firstUniqChar(string s){int countA[26] { 0 };//将该数组初始化为0方便映射不需要我们自己去初始化了以前计数排序的时候使用的是memset初始化for (auto ch : s){countA[ch - a];//按照原理下标0放a1放b2放c3放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的学习提供便利而且可以让我们做题变得更加的轻松。