目录
先写一串代码
- void test_string1()
- {
- string s1("1234");
- string::iterator it = s1.begin();
- while (it != s1.end())
- {
- *it += 1;
- cout << *it << " ";
- ++it;
- }
- }
- int main()
- {
- test_string1();
- return 0;
- }
注意
1:string是属于c++标准库的,所以要使用using namespace std;
2:使用string类的函数需要引头文件#include
3:iterator也是一个类,这个类的类域是string
我们可以把这里的迭代器当作是一个指针,*it1就表示对类中的元素进行访问,图像如图所示

- void reverse_string2()
- {
- string s1("1234");
- string::reverse_iterator rit = s1.rbegin();
- while (rit != s1.rend())
- {
- *rit += 1;
- cout << *rit << " ";
- ++rit;
- }
- }
- int main()
- {
- /*test_string1();*/
- reverse_string2();
- return 0;
- }
其中,rbegin代表的就是原来的end,rend代表的就是原来的begin
所以相当于我们是倒着进行遍历的,所以我们打印的结果应该为5 4 3 2

注意:rbegin和rend必须用反向迭代器对象来接收,begin和end必须用正向迭代器来接收。
- void print(string s)
- {
- ;
- }
假如我们要写一个打印函数,我们这样写发生传值拷贝,传值拷贝就会调用拷贝构造还会涉及深浅拷贝的问题,所以我们这里可以采取传引用的方法来减少拷贝
- void print(string& s)
- {
- ;
- }
又因为我们的打印函数只需要打印s中的值,不会对s本身发生改变,所以我们改用const来修饰引用。
- void print(const string& s)
- {
- ;
- }

这样写的话,我们就需要使用const迭代器

我们尝试调用
- void print(const string& s)
- {
- string::const_iterator it = s.begin();
- while (it != s.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
- }
- int main()
- {
- /*test_string1();*/
- /*reverse_string2();*/
- /*test_begin();*/
- string s1("1234");
- print(s1);
- return 0;
- }

在这里const迭代器的意义就是我们对于类s只能读,不可以写(修改)。

- void Print(const string& s)
- {
- string::const_reverse_iterator rit = s.rbegin();
- while (rit != s.rend())
- {
- //*it += 1;
- cout << *rit << " ";
- ++rit;
- }
- cout << endl;
- }
- int main()
- {
- /*test_string1();*/
- /*reverse_string2();*/
- /*test_begin();*/
- string s1("1234");
- //print(s1);
- Print(s1);
- return 0;
- }
我们进行编译

总结:1:正向和反向迭代器可以遍历读写容器数据
2:const正向和const反向迭代器只能读容器数据,无法对容器数据进行修改

- int main()
- {
- string s1("1234");
- cout << s1.size() << endl;
- cout << s1.length() << endl;
- return 0;
- }

打印的结果是相同的。


size()和length()本质上没有区别,都是返回容器的有效数据个数。
- int main()
- {
- string s1("1234");
- cout << s1.size() << endl;
- cout << s1.length() << endl;
- cout << s1.capacity() << endl;
- cout << s1.max_size() << endl;
- return 0;
- }

capacity()表示容器的容量
max_size()表示整型的最大数字
例如:
- int main()
- {
- string s1("1234");
- /*cout << s1.size() << endl;
- cout << s1.length() << endl;
- cout << s1.capacity() << endl;
- cout << s1.max_size() << endl;*/
- cout <<"前:"<< s1 << endl;
- s1.clear();
- cout <<"后:"<< s1 << endl;
- return 0;
- }

表示我们把数据s1的数据清空了。
- int main()
- {
- string s1("1234");
- /*cout << s1.size() << endl;
- cout << s1.length() << endl;
- cout << s1.capacity() << endl;
- cout << s1.max_size() << endl;*/
- cout <
capacity() << endl; - s1.clear();
- cout <
capacity() << endl; - return 0;
- }

我们的编译器并没有对容量进行清理。

把容器的size重写成n
例如:
- int main()
- {
- string s(100, 'x');
- s.resize(10);
- cout << s << endl;
- cout << s.capacity() << endl;
- }
我们进行编译:

我们把s的size设置成了10,所以s中只有十个元素,其他的90个元素全部被置为了空,但是注意我们的容量是不发生改变的。
resize设置的可以超过其容量,相当于扩容
例如:
- int main()
- {
- string s(100, 'x');
- s.resize(130);
- cout << s << endl;
- cout << s.capacity() << endl;
- }

例如:

对于这样的s1,他的元素个数为11,容量为15。
- int main()
- {
- string s1 = "hello world";
- s1.resize(5);
- cout << s1 << endl;
- //
- string s2 = "hello world";
- s2.resize(12);
- cout << s1 << endl;
- //
- string s3 = "hello world";
- s3.resize(20);
- cout << s1 << endl;
- }




情况1:当resize(n),n<11时,我们会删除数据。
情况2:当resize(n),n<=11<=15时,我们会插入数据,默认插入的是'\0'
情况3:当resize(n),n>15时,我们会扩容+插入数据,默认插入的是'\0'
我们也可以显示的插入数据:
- string s2 = "hello world";
- s2.resize(15,'x');
- cout << s2 << endl;
我们进行编译:

表示设置容量

例如:
- int main()
- {
- /*string s(100, 'x');
- cout << s << endl;
- cout << s.capacity() << endl;*/
- string s;
- size_t sz = s.capacity();
- cout << "making a grow:\n";
- for (int i = 0; i < 100; ++i)
- {
- s.push_back('c');
- if (sz != s.capacity())
- {
- sz = s.capacity();
- cout << "capacity changed: " << sz << '\n';
- }
- }
- }
我们进行编译:

我们发现:扩容的元素并不是固定的,比如假如我们的size为100个,我们的容量则会大于100,并且我们的扩容可能会异地扩容,造成时间和空间上的浪费,这时候我们可以使用reverse
假如我们要插入1000个数据,我们不想额外开辟,我们就先开辟1000的空间,然后进行输入
- int main()
- {
- /*string s(100, 'x');
- cout << s << endl;
- cout << s.capacity() << endl;*/
- string s;
- s.reserve(1000);
- size_t sz = s.capacity();
- cout << "making a grow:\n";
- for (int i = 0; i < 1000; ++i)
- {
- s.push_back('c');
- if (sz != s.capacity())
- {
- sz = s.capacity();
- cout << "capacity changed: " << sz << '\n';
- }
- }
- cout << sz << endl;
- }

这时候就不会产生扩容操作了。
表示取出string中的第i个元素
其实就相当于[i]
- int main()
- {
- string s1("hello world");
- cout << s1.at(1) << endl;
- }

唯一的不同就是当at如果越界时,抛异常
使用方法1:+添加的数据
- int main()
- {
- string s1("hello world");
- /*cout << s1.at(1) << endl;*/
- s1.append("!!!!!");
- cout << s1 << endl;
- }

会增添这些元素。
方法2:加添加的容器
- int main()
- {
- string s1("hello world");
- /*cout << s1.at(1) << endl;*/
- /*s1.append("!!!!!");
- cout << s1 << endl;*/
- string s2("hello world");
- s1.append(s2);
- cout << s1 << endl;
- }


- class Solution {
- public:
- string reverseOnlyLetters(string s) {
- size_t begin=0;
- size_t end=s.size()-1;
- while(begin
- {
- while(begin
isalpha(s[begin])) - {
- begin++;
- }
- while(begin
isalpha(s[end])) - {
- end--;
- }
- swap(s[begin],s[end]);
- begin++;
- end--;
- }
- return s;
- }
- };
思路:我们的主体是完成首尾交换,对于首位交换,我们一般可以用双指针的方法,如图所示

我们通过让begin++和end--来逐步实现首尾呼唤。
代码:
- class Solution {
- public:
- string reverseOnlyLetters(string s) {
- size_t begin=0;
- size_t end=s.size()-1;
- while(begin
- {
- while(begin
isalpha(s[begin])) - {
- begin++;
- }
- while(begin
isalpha(s[end])) - {
- end--;
- }
- swap(s[begin],s[end]);
- begin++;
- end--;
- }
- return s;
- }
- };
代码思想:我们要使用双指针的方法来实现首尾交换,但是我们首先需要先判断对应元素是否为字母,只有首和尾都为字母的前提下,我们才进行交换,我们可以设置while循环,当头不是字母时,begin++,然后尾不是字母时,end--,while循环走完之后对用的头和尾就都是字母了,我们进行交换,交换之后,我们让begin++,end--,跳到下一个位置,继续重复以上步骤。
注意:while循环的判断条件不仅要是非字母,并且还要begin
启示:这类首尾交换的题目一般使用双指针的写法,注意要检查越界情况
字符串中第一个唯一字符

思路:我们创建一个数组m,这个数组用来记录s中每一个元素出现的次数,然后我们从前往后遍历,如果对应数组m中对应元素有1的我们进行输出,假如没有的话,我们输出1.
- class Solution {
- public:
- int firstUniqChar(string s) {
- int m[26]={0};
- for(auto e:s)
- {
- m[e-'a']++;
- }
- for(int j=0;j
size();j++) - {
- if(m[s[j]-'a']==1)
- {
- return j;
- }
- }
- return -1;
- }
- };
代码思路:我们首先创建一个26个空间的数组为m,这个数组足够存储所有的小写字母,然后我们使用auto for把s进行遍历,把s中的每一个元素-‘a',对应的就是小写字母在m中的下标,我们让其对应的元素++。
然后我们再使用for循环,查看数组m中第一个出现1所对应的下标,如果出现了,返回对应的下标,如果没有出现,返回-1.
启示:对于这类第几次出现重复元素的问题,我们可以采用创建一个额外数组来记录次数的方式,然后再访问该数组得到结果。
字符串相加

代码:
- class Solution {
- public:
- string addStrings(string num1, string num2) {
- int end1=num1.size()-1,end2=num2.size()-1;
- string s1;
- int carry=0;
- s1.reserve(max(num1.size(),num2.size())+1);
- while(end1>=0||end2>=0)
- {
- int val1=end1>=0?num1[end1]-'0':0;
- int val2=end2>=0?num2[end2]-'0':0;
- int ret=val1+val2+carry;
- carry=ret/10;
- ret%=10;
- s1+=('0'+ret);
- end1--;
- end2--;
- }
- if(carry==1)
- {
- s1+='1';
- }
- reverse(s1.begin(),s1.end());
- return s1;
- }
- };

我们进行分析:
思路:我们要实现字符串的加减法,需要注意的是字符串中的数字字符和普通的数字是有区别的,’1'并不代表1.我们可以采取从低位到高位遍历的方法,取出每一个位对应的字符,-'0'对应的就是数字,我们再把这些数字转换为字符加起来,得到的结果就是我们要的字符串。
我们进行逐代码分析
首先创建end1和end2,分别代表num1和num2字符串最后一个元素对应的下标。
然后我们新创建一个字符串s,这个字符串用来存储我们最终计算得到的字符串。
创建变量carry=0.carry就是我们的进位,进位默认为0,例如100+20,当计算到百分位时,我们可以不访问20的百分位,让100的百分位加0即可。
然后我们提前创建好空间,因为扩容既会造成时间和空间上的浪费,我们开辟的空间是看两个字符串的长度,我们取得是两个字符串中较长的那一个,然后让其+1,例如:999+20,我们需要开辟999的位数+1个空间。
我们采取的方法是让end1和end2逐步向前遍历,从低位到高位逐渐计算,while循环的结束条件就是end1和end2同时小于0。
当end1>=0,表示我们num1字符串上end1对应的位置有元素,我们直接让这个元素-’0‘,对应的就是取出的数字val1,同理,能够取出val2.
我们的ret的结果是val1+val2+carry,不要忘记carry,因为carry是进位。
对于个分为来说,我们的carry是0,从十分位开始,我们就需要计算carry了,carry的计算方法是=ret/10,假如carry为0,表示我们ret<10,不需要进位。
然后我们让ret%=10,取出的就是进位之后的数字,我们把这个数字尾插到创建的字符串s1中,正确的方法是头插,但是我们可以用尾插然后逆置的方法。
尾插之后,让end1--,end2--来实现遍历。
当计算完毕时,我们还需要判断carry的结果,假如carry为0不处理,假如carry为1,我们尾插一个字符’1‘
然后逆置s1,返回s1即可。
启示:对于实现字符串的相加减这类题目,我们的思路是从后往前进行遍历,取出字符串对应的字符,让其-'0'对应的就是数字,让这些数字进行运算,来实现进位,然后进行尾插,然后逆置的方法。
-
相关阅读:
presto安装部署教程
Redis慢查询日志与监视器
电商独立站前端、后端、接口协议和电商API接口请求方式
End-顺序表的基本操作
【前端学习】—bind、call、apply(四)
湖仓一体架构的特性
Win11 22H2如何创建开始菜单文件夹?
1.python基础
从零实现深度学习框架——重构计算图的实现
动态代理模式下UndeclaredThrowableException的产生
-
原文地址:https://blog.csdn.net/qq_66581313/article/details/127729886