当前位置: 首页 > article >正文

C++ 之弦上舞:string 类与多样字符串操作的优雅旋律

string 类的重要性及与 C 语言字符串对比

在 C 语言中,字符串是以 '\0' 结尾的字符集合,操作字符串需借助 C 标准库的 str 系列函数,但这些函数与字符串分离,不符合 OOP 思想,且底层空间管理易出错。而在 C++ 中,string 类很好地解决了这些问题,在面试、日常工作等场景中,处理字符串相关任务时,string 类的使用更为普遍。

补充知识点:

auto 关键字:类型推导的利器

1.指针与引用类型声明

当我们用 auto 声明指针类型时,使用 auto 和 auto * 是没有任何区别的。比如:

int x = 10;
auto y = &x;
auto* z = &x;

在上述代码中,y 和 z 的类型推导结果是一样的,都是指向 int 类型的指针。

但是,当用 auto 声明引用类型时,就必须加上 &。例如:

int x = 10;
auto& m = x;

2.同一行多变量声明 

需要特别注意的是,当在同一行声明多个变量时,这些变量必须是相同的类型。因为编译器实际上只对第一个变量的类型进行推导,然后用推导出来的类型去定义其他变量。

如果类型不一致,就会导致编译报错。比如下面的代码:

auto aa = 1, bb = 2;  // 正确,都是int类型
// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
auto cc = 3, dd = 4.0; 

3.函数参数与返回值

auto 不能作为函数的参数哦,像下面这样的写法是不行的:

// 不能做参数
void func2(auto a)
{}

不过,auto 是可以作为函数的返回值的,但建议谨慎使用。例如:(C++20版本之后才能用)

// 可以做返回值,但是建议谨慎使用
auto func3()
{
    return 3;
}

4.数组声明

auto 也不能直接用来声明数组,如下所示的代码会报错:

// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
auto array[] = { 4, 5, 6 };

 5.总结

便利性

范围 for 循环:简洁的遍历方式 

传统遍历方式的痛点

#include<iostream>

int main() {
    int array[] = { 1, 2, 3, 4, 5 };

    // C++98的遍历方式
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i) {
        // 这里可以进行对数组元素的操作,比如输出
        std::cout << array[i] << " ";
    }

    std::cout << std::endl;

    return 0;
}

范围 for 循环的基本语法形式

for (元素类型 元素变量 : 可迭代对象) {
    // 在这里对元素变量进行操作,比如输出、修改等
}

范围 for 循环的便捷之处

1.自动处理迭代细节

#include<iostream>
#include <string>

int main() {
    std::string str("hello world");

    // 使用范围for循环遍历字符串
    for (auto ch : str) {
        std::cout << ch << " ";
    }

    std::cout << std::endl;

    return 0;
}

2.适用于多种数据结构 

#include<iostream>
#include <string>
#include <map>

int main() {
    std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} };

    // 使用范围for循环遍历map
    for (auto& it : dict) {
        std::cout << it.first << ":" << it.second << std::endl;
    }

    return 0;
}

3. 简洁的代码风格 

传统方式:

#include<iostream>
#include <vector>

int main() {
    std::vector<int> vec = { 1, 2, 3, 4, 5 };

    // 传统的遍历方式,使用迭代器
    std::vector<int>::iterator it;
    for (it = vec.begin(); it!= vec.end(); ++it) {
        std::cout << *it << " ";
    }

    std::cout << std::endl;

    return 0;
}

范围 for 循环方式: 

#include<iostream>
#include <vector>

int main() {
    std::vector<int> vec = { 1, 2, 3, 4, 5 };

    // 使用范围for循环遍历
    for (auto num : vec) {
        std::cout << num << " ";
    }

    std::cout << std::endl;

    return 0;
}

迭代器:容器遍历好帮手 

概念与作用

迭代器类似指针,能统一访问容器元素,无需关心容器具体实现,方便遍历各种容器。

分类

正向迭代器:按元素存储顺序从首到尾遍历,如std::string遍历示例。

#include <iostream>
#include <string>

int main() {
    std::string str = "Hello World";

    // 使用正向迭代器遍历string
    std::string::iterator it;
    for (it = str.begin(); it!= str.end(); ++it) {
        std::cout << *it;
    }

    std::cout << std::endl;

    return 0;
}

反向迭代器:从容器尾到头遍历,像std::string的反向遍历代码。

#include <iostream>
#include <string>

int main() {
    std::string str = "Hello World";

    // 使用反向迭代器遍历string
    std::string::reverse_iterator rit;
    for (rit = str.rbegin(); rit!= str.rend(); ++rit) {
        std::cout << *rit;
    }

    std::cout << std::endl;

    return 0;
}

常量迭代器:用于不修改元素的遍历,如常量std::string的情况。

#include <iostream>
#include <string>

int main() {
    const std::string str = "Hello World";

    // 使用常量迭代器遍历常量string
    std::string::const_iterator cit;
    for (cit = str.begin(); cit!= str.end(); ++cit) {
        std::cout << *cit;
    }

    std::cout << std::endl;

    return 0;
}

因为str被声明为常量,所以必须用常量迭代器cit来遍历,若用普通迭代器尝试修改字符会报错。

常用操作 

解引用(*):获取迭代器指向元素的值。

std::cout << *it << " ";

这里的*it就是获取迭代器it所指向的整数元素的值,并将其输出。 

递增递减(++、--):移动迭代器位置。

for (it = str.begin(); it!= str.end(); ++it)

比较(==、!=):判断迭代器位置关系。

for (it = string.begin(); it!= string.end(); ++it) {
    //...
}

string 类的基础特性

string类对象的常见构造

string 类实现了多个构造函数重载,常用构造函数如下:

string();  //构造一个空字符串

string(const char* s);  //复制s所指的字符序列

string(const char* s, size_t n);  //复制s所指字符序列的前n个字符

string(size_t n, char c);  //生成n个c字符的字符串

string(const string& str);  //生成str的复制品

string(const string& str, size_t pos, size_t len = npos);  //复制str中从字符位置pos开始并跨越len个字符的部分

 使用方法:

string s1;                     //构造空字符串
string s2("hello string");     //复制"hello string"
string s3("hello string", 3);  //复制"hello string"的前3个字符
string s4(10, 's');            //生成10个's'字符的字符串
string s5(s2);                 //生成s2的复制品
string s6(s2, 0, 4);           //复制s2中从字符位置0开始并跨越4个字符的部分

string 类大小和容量相关操作

获取有效字符个数size() length()

使用size()length()函数,二者底层实现原理相同,一般常用size()

string s("CSDN");
cout << s.size() << endl; //4
cout << s.length() << endl; //4

获取最大可包含字符数max_size()

通过max_size()函数获取。

string s("CSDN");
cout << s.max_size() << endl; //4294967294

获取分配的存储空间大小capacity()

capacity()函数可获取当前对象所分配的存储空间大小。

string s("CSDN");
cout << s.capacity() << endl; //15

改变有效字符个数resize()

resize()函数可改变当前对象的有效字符个数,遵循一定规则,如 n 大于当前 size 时扩大,小于时缩小。

string s("CSDN");
s.reserve(20); 
cout << s << endl; //CDSN
cout << s.size() << endl; //4
cout << s.capacity() << endl; //31

改变容量大小reserve()

reserve()函数用于改变当前对象的容量大小,当 n 大于当前 capacity 时扩大,小于时不做处理。

string s("CSDN");
s.reserve(20); 
cout << s << endl; //CDSN
cout << s.size() << endl; //4
cout << s.capacity() << endl; //31

删除对象内容clear()

clear()函数可删除对象内容,使其变为空字符串。

string s("CSDN");
s.clear();
cout << s << endl; //空字符串

判断对象是否为空empty()

empty()函数用于判断对象是否为空,返回布尔值。

string s("CSDN");
cout << s.empty() << endl; //0
s.clear();
cout << s.empty() << endl; //1

string 类的常见操作

string类对象的修改操作

插入操作

1.尾插push_back()

使用push_back()函数可在字符串末尾插入单个字符。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s;
    s.push_back('C');
    s.push_back('S');
    s.push_back('D');
    s.push_back('N');
    cout << s << endl; //CSDN
    return 0;
}

2.指定位置插入insert()

insert()函数有多种重载形式,可在指定位置插入字符串、字符等。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s("C"); //C

    //insert(pos, str)在pos位置插入字符串str
    s.insert(1, "S"); //CS

    //insert(pos, string)在pos位置插入string对象
    string t("D");
    s.insert(2, t); //CSD

    //insert(pos, char)在pos位置插入字符char
    s.insert(s.end(), 'N'); //CSDN

    cout << s << endl; //CSDN
    return 0;
}

3.拼接操作:append()

使用append()函数可完成 string 的拼接,有多种重载形式,可拼接 string 对象、字符串、多个相同字符等。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s1("I");
    string s2(" like");

    //append(string)完成两个string对象的拼接
    s1.append(s2); //I like

    //append(str)完成string对象和字符串str的拼接
    s1.append(" C++"); //I like C++

    //append(n, char)将n个字符char拼接到string对象后面
    s1.append(3, '!'); //I like C++!!!

    cout << s1 << endl; //I like C++!!!
    return 0;
}

4.operator+=

其实上面的append和push_back我们除了特定的方法我们是不会使用到的

我们使用+=就行了

int main()
{
	string s3("hello");
	s3 += ',';
	s3 += "world";
	cout << s3 << endl;//hello,world
	return 0;
}

删除操作

1.尾删pop_back()

pop_back()函数可删除字符串末尾的字符。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s("C++");
    s.pop_back();
    s.pop_back();
    cout << s << endl; //C
    return 0;
}

2.指定位置删除erase()

erase()函数有多种重载形式,可删除指定位置、指定范围的字符。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s("I like C++!!!");

    //erase(pos, n)删除pos位置开始的n个字符
    s.erase(8, 5); //I like C

    //erase(pos)删除pos位置的字符
    s.erase(s.end()-1); //I like

    //erase(pos1, pos2)删除[pos1, pos2)上所有字符
    s.erase(s.begin() + 1, s.end()); //I

    cout << s << endl; //I
    return 0;
}

查找操作

1.正向查找find()

find()函数可正向搜索第一个匹配项,可查找 string 对象、字符串、字符等。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s1("http://www.cplusplus.com/reference/string/string/find/");

    //find(string)正向搜索与string对象所匹配的第一个位置
    string s2("www");
    size_t pos1 = s1.find(s2);
    cout << pos1 << endl; //7

    //find(str)正向搜索与字符串str所匹配的第一个位置
    char str[] = "cplusplus.com";
    size_t pos2 = s1.find(str);
    cout << pos2 << endl;  //11

    //find(char)正向搜索与字符char所匹配的第一个位置
    size_t pos3 = s1.find(':');
    cout << pos3 << endl; //4
    return 0;
}

2.反向查找rfind()

rfind()函数可反向搜索第一个匹配项,同样可查找多种类型。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s1("http://www.cplusplus.com/reference/string/string/find/");

    //rfind(string)反向搜索与string对象所匹配的第一个位置
    string s2("string");
    size_t pos1 = s1.rfind(s2);
    cout << pos1 << endl; //42

    //rfind(str)反向搜索与字符串str所匹配的第一个位置
    char str[] = "reference";
    size_t pos2 = s1.rfind(str);
    cout << pos2 << endl;  //25

    //rfind(char)反向搜索与字符char所匹配的第一个位置
    size_t pos3 = s1.rfind('/');
    cout << pos3 << endl; //53
    return 0;
}

比较操作 compare()

使用compare()函数完成比较,有多种重载形式,根据比较规则返回不同结果。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s1("hello world");
    string s2("hello CSDN");

    //"hello world"和"hello CSDN"比较
    cout << s1.compare(s2) << endl; //1

    //"ell"和"hello CSDN"比较
    cout << s1.compare(1, 3, s2) << endl; //-1

    //"hello"和"hello"比较
    cout << s1.compare(0, 4, s2, 0, 4) << endl; //0

    return 0;
}

替换操作 replace()

使用replace()函数完成 string 的替换,有多种重载形式,可将指定位置的字符或字符序列替换为其他内容。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s("hello world");

    //replace(pos, len, str)将pos位置开始的len个字符替换为字符串str
    s.replace(6, 4, "CSDN"); //hello CSDNd

    //replace(pos, len, n, char)将pos位置开始的len个字符替换为n个字符char
    s.replace(10, 1, 3, '!'); //hello CSDN!!!

    cout << s << endl;
    return 0;
}

交换操作 swap()

使用swap()函数可完成两个 string 类的交换,有成员函数和非成员函数两种形式。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s1("hello");
    string s2("CSDN");

    //使用string类的成员函数swap交换s1和s2
    s1.swap(s2);
    cout << s1 << endl; //CSDN
    cout << s2 << endl; //hello

    //使用非成员函数swap交换s1和s2
    swap(s1, s2);
    cout << s1 << endl; //hello
    cout << s2 << endl; //CSDN
    return 0;
}

string 类中元素的访问

string类对象的访问及遍历操作

通过下标访问 operator[]

string 类对[ ]运算符进行了重载,可直接使用[ ] +下标访问和修改对象中的元素。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s("CSDN");
    //[]+下标访问对象元素
    for (size_t i = 0; i < s.size(); i++)
    {
        cout << s[i];
    }
    cout << endl;

    //[]+下标修改对象元素内容
    for (size_t i = 0; i < s.size(); i++)
    {
        s[i] = 'x';
    }
    cout << s << endl; //xxxx
    return 0;
}

通过 at 函数访问 at

at函数也可访问和修改对应位置的元素。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s("CSDN");
    for (size_t i = 0; i < s.size(); i++)
    {
        //at(pos)访问pos位置的元素
        cout << s.at(i);
    }
    cout << endl;

    for (size_t i = 0; i < s.size(); i++)
    {
        //at(pos)访问pos位置的元素,并对其进行修改
        s.at(i) = 'x';
    }
    cout << s << endl; //xxxx
    return 0;
}

通过范围 for 访问 for

若要通过范围 for 修改对象元素,接收元素的变量 e 的类型必须是引用类型。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s("CSDN");
    //使用范围for访问对象元素
    for (auto e : s)
    {
        cout << e;
    }
    cout << endl; //CSDN

    //使用范围for访问对象元素,并对其进行修改
    for (auto& e : s) //需要修改对象的元素,e必须是引用类型
    {
        e = 'x';
    }
    cout << s << endl; //xxxx
    return 0;
}

通过迭代器访问

可使用迭代器访问和修改对象中的元素。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s("CSDN");

    //使用迭代器访问对象元素
    string::iterator it1 = s.begin();
    while (it1!= s.end())
    {
        cout << *it1;
        it1++;
    }
    cout << endl; //CSDN

    //使用迭代器访问对象元素,并对其进行修改
    string::iterator it2 = s.begin();
    while (it2!= s.end())
    {
        *it2 += 1;
        it2++;
    }
    cout << s << endl; //DTEO
    return 0;
}

string 类中运算符的使用

string类非成员函数

赋值运算符(=)

string 类对=运算符进行了重载,支持 string 类、字符串、字符的赋值。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s1;
    string s2("CSDN");

    //支持string类的赋值
    s1 = s2;
    cout << s1 << endl; //CSDN

    //支持字符串的赋值
    s1 = "hello";
    cout << s1 << endl;  //hello

    //支持字符的赋值
    s1 = 'x';
    cout << s1 << endl; //x
    return 0;
}

复合赋值运算符(+=)

+=运算符进行了重载,支持多种类型的复合赋值。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s1;
    string s2("hello");

    //支持string类的复合赋值
    s1 += s2;
    cout << s1 << endl; //hello

    //支持字符串的复合赋值
    s1 += " CSDN";
    cout << s1 << endl; //hello CSDN

    //支持字符的复合赋值
    s1 += '!';
    cout << s1 << endl; //hello CSDN!
    return 0;
}

加法运算符(+)

重载后的+运算符支持多种类型的相加操作,相加后均返回一个 string 类对象。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s;
    string s1("super");
    string s2("man");
    char str[] = "woman";
    char ch = '!';

    //string类 + string类
    s = s1 + s2;
    cout << s << endl; //superman

    //string类 + 字符串
    s = s1 + str;
    cout << s << endl; //superwoman

    //字符串 + string类
    s = str + s1;
    cout << s << endl; //womansuper

    //string类 + 字符
    s = s1 + ch;
    cout << s << endl; //super!

    //字符 + string类
    s = ch + s1;
    cout << s << endl; //!super
    return 0;
}

输入输出运算符(>> 和 <<)

>><<运算符进行了重载,可直接用于 string 类的输入和输出。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s;
    cin >> s; //输入
    cout << s << endl; //输出
    return 0;
}

关系运算符

对一系列关系运算符(==、!=、<、<=、>、>=)进行了重载,支持多种类型之间的关系比较,比较的是对应字符的 ASCII 码值。

牛刀小试

一、仅仅反转字母

题目描述
给定一个字符串,要返回 “反转后的” 字符串,其中不是字母的字符都保留在原地,而所有字母的位置发生反转。比如输入 “a-bC-dEf-ghIj”,输出应为 “j-Ih-gfE-dCba”。

思路
这里采用了双指针的方法。开始时,头指针指向字符串开头,尾指针指向字符串末尾('\0' 的前一个字符)。头指针先向后寻找待反转字母,尾指针再向前寻找待反转字母,然后反转这两个待反转字母。重复这个步骤,直到头指针和尾指针发生错位为止。

代码实现

class Solution {
public:
    //判断字符ch是否是字母
    bool IsLetter(char ch) {
        if ((ch >= 'a' && ch <= 'z') 
            || (ch >= 'A' && ch <= 'Z'))
            return true;
        else
            return false;
    }

    //仅仅反转字母
    string reverseOnlyLetters(string s) {
        if (s.empty()) //若s为空字符串,则返回其本身
            return s;

        size_t begin = 0, 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; //返回反转后的字符串
    }
};

二、字符串中的第一个唯一字

题目描述
给定一个字符串,要找到它的第一个不重复的字母,并返回它的索引。如果不存在,则返回 -1。例如,对于字符串 “loveleetcode”,应返回 2 。

思路
用一个含有 26 个元素的数组来统计 26 个字母在字符串中出现的次数,然后再遍历一次字符串,寻找字符串中第一个只出现一次的字母,并返回它的索引,若不存在,则返回 -1 。

代码实现

class Solution {
public:
    int firstUniqChar(string s) {
        size_t table[26] = { 0 };
        //统计26个字母在字符串中出现的次数
        for (size_t i = 0; i < s.size(); i++) {
            table[s[i] - 'a']++;
        }
        //寻找字符串中的第一个只出现一次字母
        for (size_t i = 0; i < s.size(); i++) {
            if (table[s[i] - 'a'] == 1)
                return i; //返回下标索引
        }
        return -1; //不存在,返回-1
    }
};

三、字符串最后一个单词的长度

题目描述
要计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于 5000 。比如输入 “hello CSDN”,输出应为 4 。

思路
先找到字符串中最后一个空格的位置,空格之后的字符个数就是最后一个单词的长度。若字符串中不存在空格,则字符串的长度就是最后一个单词的长度。这里的重点是读取含有空格的字符串,因为操作符 >> 读取到空格便会停止,所以要用到 getline 函数来读取目标字符串。

代码实现

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    getline(cin, s); //从cin读取一行含有空格的字符串
    size_t pos = s.rfind(' '); //获取字符串中最后一个空格的位置
    if (pos == string::npos) //字符串中不含空格
    {
        //输出字符串的长度
        cout << s.size() << endl;
    }
    else //字符串中含有空格
    {
        //输出字符串的长度 - 最后一次出现空格的位置 - 1
        cout << s.size() - pos - 1 << endl;
    }
    return 0;
}

四、验证回文串

题目描述
给定一个字符串,要验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。例如输入 “A man, a plan, a canal: Panama”,输出应为 true 。

思路
首先将字符串中所有的大写字母转换为小写字母,然后使用双指针方法。头指针指向字符串开头,尾指针指向字符串末尾('\0' 的前一个字符)。头指针先向后寻找待判断的字母或数字字符,尾指针再向前寻找待判断的字母或数字字符,然后判断这两个字符是否相等,若相等,则继续下一次判断;若不相等,则该字符串不是回文串。重复这个步骤,直到头指针和尾指针发生错位为止,此时便可确定该字符串是回文串。

代码实现

class Solution {
public:
    //判断ch是否是小写字母或数字字符
    bool isLetterOrNumber(char ch) {
        if ((ch >= 'a' && ch <= 'z')
            || (ch >= '0' && ch <= '9'))
            return true;
        else
            return false;
    }

    //验证回文串
    bool isPalindrome(string s) {
        //将字符串中所有大写字母转为小写字母
        for (auto& ch : s) {
            if (ch >= 'A' && ch <= 'Z')
                ch += 32;
        }
        int begin = 0, end = s.size() - 1; //定义头指针和尾指针,用于验证回文串
        while (begin < end) //当还有字母未判断时,循环继续
        {
            while (begin < end &&!isLetterOrNumber(s[begin]) //头指针寻找待判断的字母或数字字符
                begin++;

            while (begin < end &&!isLetterOrNumber(s[end]) //尾指针寻找待判断的字母或数
                end--;

            if (s[begin] == s[end]) //待判断字符相等,继续下一次判断
            {
                begin++;
                end--;
            }
            else
            {
                return false; //待判断字符不相等,该字符串不是回文串
            }
        }
        return true; //字符串中所有字符判断完毕,该字符串是回文串
    }
};

五、字符串相加

题目描述
给定两个字符串形式的非负整数 num1 和 num2,要计算它们的和。比如输入 “999”,“1”,输出应为 “1000”。

思路
和平时计算两个数的和类似,从两个字符串的最后一个数字开始进行相加,并设置一个变量记录是否需要进位。每一位置的数字 = 字符串 1 对应位置的数字 + 字符串 2 对应位置的数字 + 进位变量,若相加后该位置的数字大于 9,则说明需要进位,这时设置进位变量为 1,并将该位置的数字减去 10 后的结果作为相加后该位置的数字。如此进行下去,直到两个字符串都遍历完毕。特别注意,两个字符串相加结束后还需要判断进位变量是否为 1,若为 1,则需要头插一个字符 1 到最终的字符串中。

原始代码实现

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1 = num1.size() - 1, end2 = num2.size() - 1; //定义两个字符串的尾指针
        string RetStr; //存储两个字符串相加后的结果
        int next = 0; //标识进位
        while (end1 >= 0 || end2 >= 0) //两个字符串中有一个未遍历完,则继续循环
        {
            int val1 = 0; //第一个字符串等待相加的数字
            if (end1 >= 0)
            {
                val1 = num1[end1] - '0';
                end1--;
            }
            int val2 = 0; //第二个字符串等待相加的数字
            if (end2 >= 0)
                val2 = num2[end2] - '0';
            end2--;
            int RetVal = val1 + val2 + next; //两个数字相加后的结果,注意需要加上进位
            if (RetVal > 9) //判断是否需要进位
            {
                RetVal -= 10;
                next = 1; //需要进位,设置next为1
            }
            else
            {
                next = 0; //不需进位,设置next为0
            }
            RetStr.insert(0, 1, RetVal + '0'); //将RetVal头插到RetStr
        }
        if (next == 1) //判断是否还需进位
            RetStr.insert(0, 1, '1'); //将'1'头插到RetStr
        return RetStr; //返回这两个字符串相加后的结果
    }
};

优化思路及代码
原始代码中每得到一个位置的结果就头插一个数字到最终的字符串中,时间复杂度较高。优化的方法是将得到的每一位数字都尾插到字符串后面,只需最后进行一次字符串反转即可。

优化后的代码:

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1 = num1.size() - 1, end2 = num2.size() - 1; //定义两个字符串的尾指针
        string RetStr; //存储两个字符串相加后的结果
        int next = 0; //标识进位
        while (end1 >= 0 || end2 >= 0) //两个字符串中有一个未遍历完,则继续循环
        {
            int val1 = 0; //第一个字符串等待相加的数字
            if (end1 >= 0)
            {
                val1 = num1[end1] - '0';
                end1--;
            }
            int val2 = 0; //第二个字符串等待相加的数字
            if (end2 >= 0)
                val2 = num2[end2] - '0';
            end2--;
            int RetVal = val1 + val2 + next; //两个数字相加后的结果,注意需要加上进位
            if (RetVal > 9) //判断是否需要进位
            {
                RetVal -= 10;
                next = 1; //需要进位,设置next为1
            }
            else
            {
                next = 0; //不需进位,设置next为0
            }
            RetStr += (RetVal + '0'); //将RetVal尾插到RetStr
        }
        if (next == 1) //判断是否还需进位
            RetStr += '1'; //将'1'尾插插到RetStr
        reverse(RetStr.begin(), RetStr.end()); //将字符串RetStr进行反转
        return RetStr; //返回这两个字符串相加后的结果
    }
};

http://www.kler.cn/a/416860.html

相关文章:

  • vue超过三行显示省略号和查看更多按钮
  • ICPM端口的用途是什么?
  • 编译以前项目更改在x64下面时报错:函数“PVOID GetCurrentFiber(void)”已有主体
  • 如何快速上手UPR ---查看资源检测报告
  • SQL进阶——子查询与视图
  • 鸿蒙学习自由流转与分布式运行环境-价值与架构定义(1)
  • 面向数字音视频的网络与操作系统技术研讨会 征稿通知
  • Qt 项目中同时使用 CMAKE_AUTOUIC 和 UiTools 的注意事项
  • 泷羽Sec-星河飞雪-BurpSuite之解码、日志、对比模块基础使用
  • 频繁发生Full GC的原因有哪些?如何避免发生Full GC
  • vue3创建
  • 使用PyQt5开发一个GUI程序的实例演示
  • 解决“磁盘已插上,但Windows系统无法识别“问题
  • 记一次 .NET某hdp智能柜系统 卡死分析
  • el-selet下拉菜单自定义内容,下拉内容样式类似表格
  • ChatGPT的应用场景:开启无限可能的大门
  • apache实现绑定多个虚拟主机访问服务
  • Vue项目运行步骤(详细图解)
  • 静态页面 和 动态页面(Java Web开发)
  • 【Linux网络编程】第三弹---UDP网络通信深度解析:构建服务器端、客户端,并实现两端通信的完整步骤与测试
  • 【传感器技术】第6章 压电式传感器
  • [python脚本处理文件入门]-18.使用Python进行PDF文件的合并与拆分
  • 浅谈volatile
  • Mybatis:CRUD数据操作之修改数据update
  • 【QT/MinGW/.a->.lib】如何将一个用QT的MingGW编译dll项目出的dll文件导出一份.lib文件给其他项目链接动态库用
  • docker启动容器,语句名词解释