• string的应用和练习


    目录

    迭代器iterator

     反向迭代器reverse_interator

    const迭代器

     反向const迭代器

     string对象的容量操作

     size()和length()

    capacity()和max_size()

    clear清空数据

    clear会清空数据,会不会清空容量?

    resize()

    resize的三种情况:

     reserve

    at(i)

    append:增补容器

     关于string的oj题目。

    仅仅反转字母

    字符串中第一个唯一字符

    字符串相加


    迭代器iterator

    先写一串代码

    1. void test_string1()
    2. {
    3. string s1("1234");
    4. string::iterator it = s1.begin();
    5. while (it != s1.end())
    6. {
    7. *it += 1;
    8. cout << *it << " ";
    9. ++it;
    10. }
    11. }
    12. int main()
    13. {
    14. test_string1();
    15. return 0;
    16. }

    注意

    1:string是属于c++标准库的,所以要使用using namespace std;

    2:使用string类的函数需要引头文件#include

    3:iterator也是一个类,这个类的类域是string

    我们可以把这里的迭代器当作是一个指针,*it1就表示对类中的元素进行访问,图像如图所示

     反向迭代器reverse_interator

    1. void reverse_string2()
    2. {
    3. string s1("1234");
    4. string::reverse_iterator rit = s1.rbegin();
    5. while (rit != s1.rend())
    6. {
    7. *rit += 1;
    8. cout << *rit << " ";
    9. ++rit;
    10. }
    11. }
    12. int main()
    13. {
    14. /*test_string1();*/
    15. reverse_string2();
    16. return 0;
    17. }

    其中,rbegin代表的就是原来的end,rend代表的就是原来的begin

    所以相当于我们是倒着进行遍历的,所以我们打印的结果应该为5 4 3 2

     注意:rbegin和rend必须用反向迭代器对象来接收,begin和end必须用正向迭代器来接收。

    const迭代器

    1. void print(string s)
    2. {
    3. ;
    4. }

    假如我们要写一个打印函数,我们这样写发生传值拷贝,传值拷贝就会调用拷贝构造还会涉及深浅拷贝的问题,所以我们这里可以采取传引用的方法来减少拷贝

    1. void print(string& s)
    2. {
    3. ;
    4. }

    又因为我们的打印函数只需要打印s中的值,不会对s本身发生改变,所以我们改用const来修饰引用。

    1. void print(const string& s)
    2. {
    3. ;
    4. }

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

     我们尝试调用

    1. void print(const string& s)
    2. {
    3. string::const_iterator it = s.begin();
    4. while (it != s.end())
    5. {
    6. cout << *it << " ";
    7. ++it;
    8. }
    9. cout << endl;
    10. }
    11. int main()
    12. {
    13. /*test_string1();*/
    14. /*reverse_string2();*/
    15. /*test_begin();*/
    16. string s1("1234");
    17. print(s1);
    18. return 0;
    19. }

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

     反向const迭代器

    1. void Print(const string& s)
    2. {
    3. string::const_reverse_iterator rit = s.rbegin();
    4. while (rit != s.rend())
    5. {
    6. //*it += 1;
    7. cout << *rit << " ";
    8. ++rit;
    9. }
    10. cout << endl;
    11. }
    12. int main()
    13. {
    14. /*test_string1();*/
    15. /*reverse_string2();*/
    16. /*test_begin();*/
    17. string s1("1234");
    18. //print(s1);
    19. Print(s1);
    20. return 0;
    21. }

    我们进行编译

     总结:1:正向和反向迭代器可以遍历读写容器数据

    2:const正向和const反向迭代器只能读容器数据,无法对容器数据进行修改

     string对象的容量操作

     size()和length()

    1. int main()
    2. {
    3. string s1("1234");
    4. cout << s1.size() << endl;
    5. cout << s1.length() << endl;
    6. return 0;
    7. }

     打印的结果是相同的。

     

    size()和length()本质上没有区别,都是返回容器的有效数据个数。

    capacity()和max_size()

    1. int main()
    2. {
    3. string s1("1234");
    4. cout << s1.size() << endl;
    5. cout << s1.length() << endl;
    6. cout << s1.capacity() << endl;
    7. cout << s1.max_size() << endl;
    8. return 0;
    9. }

     capacity()表示容器的容量

    max_size()表示整型的最大数字

    clear清空数据

    例如:

    1. int main()
    2. {
    3. string s1("1234");
    4. /*cout << s1.size() << endl;
    5. cout << s1.length() << endl;
    6. cout << s1.capacity() << endl;
    7. cout << s1.max_size() << endl;*/
    8. cout <<"前:"<< s1 << endl;
    9. s1.clear();
    10. cout <<"后:"<< s1 << endl;
    11. return 0;
    12. }

     表示我们把数据s1的数据清空了。

    clear会清空数据,会不会清空容量?

    1. int main()
    2. {
    3. string s1("1234");
    4. /*cout << s1.size() << endl;
    5. cout << s1.length() << endl;
    6. cout << s1.capacity() << endl;
    7. cout << s1.max_size() << endl;*/
    8. cout <capacity() << endl;
    9. s1.clear();
    10. cout <capacity() << endl;
    11. return 0;
    12. }

     

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

    resize()

     把容器的size重写成n

    例如:

    1. int main()
    2. {
    3. string s(100, 'x');
    4. s.resize(10);
    5. cout << s << endl;
    6. cout << s.capacity() << endl;
    7. }

    我们进行编译:

     我们把s的size设置成了10,所以s中只有十个元素,其他的90个元素全部被置为了空,但是注意我们的容量是不发生改变的。

    resize设置的可以超过其容量,相当于扩容

    例如:

    1. int main()
    2. {
    3. string s(100, 'x');
    4. s.resize(130);
    5. cout << s << endl;
    6. cout << s.capacity() << endl;
    7. }

    resize的三种情况:

    例如:

     对于这样的s1,他的元素个数为11,容量为15。

    1. int main()
    2. {
    3. string s1 = "hello world";
    4. s1.resize(5);
    5. cout << s1 << endl;
    6. //
    7. string s2 = "hello world";
    8. s2.resize(12);
    9. cout << s1 << endl;
    10. //
    11. string s3 = "hello world";
    12. s3.resize(20);
    13. cout << s1 << endl;
    14. }

     

     

     情况1:当resize(n),n<11时,我们会删除数据。

    情况2:当resize(n),n<=11<=15时,我们会插入数据,默认插入的是'\0'

    情况3:当resize(n),n>15时,我们会扩容+插入数据,默认插入的是'\0'

    我们也可以显示的插入数据:

    1. string s2 = "hello world";
    2. s2.resize(15,'x');
    3. cout << s2 << endl;

     我们进行编译:

     reserve

    表示设置容量

    例如:

    1. int main()
    2. {
    3. /*string s(100, 'x');
    4. cout << s << endl;
    5. cout << s.capacity() << endl;*/
    6. string s;
    7. size_t sz = s.capacity();
    8. cout << "making a grow:\n";
    9. for (int i = 0; i < 100; ++i)
    10. {
    11. s.push_back('c');
    12. if (sz != s.capacity())
    13. {
    14. sz = s.capacity();
    15. cout << "capacity changed: " << sz << '\n';
    16. }
    17. }
    18. }

    我们进行编译:

     我们发现:扩容的元素并不是固定的,比如假如我们的size为100个,我们的容量则会大于100,并且我们的扩容可能会异地扩容,造成时间和空间上的浪费,这时候我们可以使用reverse

    假如我们要插入1000个数据,我们不想额外开辟,我们就先开辟1000的空间,然后进行输入

    1. int main()
    2. {
    3. /*string s(100, 'x');
    4. cout << s << endl;
    5. cout << s.capacity() << endl;*/
    6. string s;
    7. s.reserve(1000);
    8. size_t sz = s.capacity();
    9. cout << "making a grow:\n";
    10. for (int i = 0; i < 1000; ++i)
    11. {
    12. s.push_back('c');
    13. if (sz != s.capacity())
    14. {
    15. sz = s.capacity();
    16. cout << "capacity changed: " << sz << '\n';
    17. }
    18. }
    19. cout << sz << endl;
    20. }

     这时候就不会产生扩容操作了。

    at(i)

    表示取出string中的第i个元素

    其实就相当于[i]

    1. int main()
    2. {
    3. string s1("hello world");
    4. cout << s1.at(1) << endl;
    5. }

     唯一的不同就是当at如果越界时,抛异常

    append:增补容器

    使用方法1:+添加的数据

    1. int main()
    2. {
    3. string s1("hello world");
    4. /*cout << s1.at(1) << endl;*/
    5. s1.append("!!!!!");
    6. cout << s1 << endl;
    7. }

     会增添这些元素。

    方法2:加添加的容器

    1. int main()
    2. {
    3. string s1("hello world");
    4. /*cout << s1.at(1) << endl;*/
    5. /*s1.append("!!!!!");
    6. cout << s1 << endl;*/
    7. string s2("hello world");
    8. s1.append(s2);
    9. cout << s1 << endl;
    10. }

     关于string的oj题目。

    仅仅反转字母

    力扣

    1. class Solution {
    2. public:
    3. string reverseOnlyLetters(string s) {
    4. size_t begin=0;
    5. size_t end=s.size()-1;
    6. while(begin
    7. {
    8. while(beginisalpha(s[begin]))
    9. {
    10. begin++;
    11. }
    12. while(beginisalpha(s[end]))
    13. {
    14. end--;
    15. }
    16. swap(s[begin],s[end]);
    17. begin++;
    18. end--;
    19. }
    20. return s;
    21. }
    22. };

     思路:我们的主体是完成首尾交换,对于首位交换,我们一般可以用双指针的方法,如图所示

     我们通过让begin++和end--来逐步实现首尾呼唤。

    代码:

    1. class Solution {
    2. public:
    3. string reverseOnlyLetters(string s) {
    4. size_t begin=0;
    5. size_t end=s.size()-1;
    6. while(begin
    7. {
    8. while(beginisalpha(s[begin]))
    9. {
    10. begin++;
    11. }
    12. while(beginisalpha(s[end]))
    13. {
    14. end--;
    15. }
    16. swap(s[begin],s[end]);
    17. begin++;
    18. end--;
    19. }
    20. return s;
    21. }
    22. };

    代码思想:我们要使用双指针的方法来实现首尾交换,但是我们首先需要先判断对应元素是否为字母,只有首和尾都为字母的前提下,我们才进行交换,我们可以设置while循环,当头不是字母时,begin++,然后尾不是字母时,end--,while循环走完之后对用的头和尾就都是字母了,我们进行交换,交换之后,我们让begin++,end--,跳到下一个位置,继续重复以上步骤。

    注意:while循环的判断条件不仅要是非字母,并且还要begin

    启示:这类首尾交换的题目一般使用双指针的写法,注意要检查越界情况

    字符串中第一个唯一字符

    力扣

     思路:我们创建一个数组m,这个数组用来记录s中每一个元素出现的次数,然后我们从前往后遍历,如果对应数组m中对应元素有1的我们进行输出,假如没有的话,我们输出1.

    1. class Solution {
    2. public:
    3. int firstUniqChar(string s) {
    4. int m[26]={0};
    5. for(auto e:s)
    6. {
    7. m[e-'a']++;
    8. }
    9. for(int j=0;jsize();j++)
    10. {
    11. if(m[s[j]-'a']==1)
    12. {
    13. return j;
    14. }
    15. }
    16. return -1;
    17. }
    18. };

    代码思路:我们首先创建一个26个空间的数组为m,这个数组足够存储所有的小写字母,然后我们使用auto for把s进行遍历,把s中的每一个元素-‘a',对应的就是小写字母在m中的下标,我们让其对应的元素++。

    然后我们再使用for循环,查看数组m中第一个出现1所对应的下标,如果出现了,返回对应的下标,如果没有出现,返回-1.

    启示:对于这类第几次出现重复元素的问题,我们可以采用创建一个额外数组来记录次数的方式,然后再访问该数组得到结果。

    字符串相加

    力扣

    代码:

    1. class Solution {
    2. public:
    3. string addStrings(string num1, string num2) {
    4. int end1=num1.size()-1,end2=num2.size()-1;
    5. string s1;
    6. int carry=0;
    7. s1.reserve(max(num1.size(),num2.size())+1);
    8. while(end1>=0||end2>=0)
    9. {
    10. int val1=end1>=0?num1[end1]-'0':0;
    11. int val2=end2>=0?num2[end2]-'0':0;
    12. int ret=val1+val2+carry;
    13. carry=ret/10;
    14. ret%=10;
    15. s1+=('0'+ret);
    16. end1--;
    17. end2--;
    18. }
    19. if(carry==1)
    20. {
    21. s1+='1';
    22. }
    23. reverse(s1.begin(),s1.end());
    24. return s1;
    25. }
    26. };

     我们进行分析:

    思路:我们要实现字符串的加减法,需要注意的是字符串中的数字字符和普通的数字是有区别的,’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