C++ string(2)

在这里插入图片描述

1.初识迭代器和范围for

1.1迭代器

上次我们知道了可以通过operator[]重载了后,可以直接访问字符串:

Test01()
{
	string s1;
	string s2("hello world");
	s2[0] = 'x';
	cout << s1 << s2 << endl;
	//下标加[]重载
	for (size_t i = 0; i < s2.size(); i++)
	{
		cout << s2[i] << " " ;
	}
	cout << endl;
}

而我们也可以通过迭代器来实现,实现的方式与指针相仿,而且所有的容器都能使用:

string::iterator it = s2.begin();
while (it != s2.end())
{
		cout << *it << " ";
		++it;
}
cout << endl;

image-20241014184335471

也有反向的迭代器:

string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
	cout << *rit << " ";
	++rit;
}
cout << endl;

当然结果也是相反的:

image-20241016194503303

但是这样里面的内容可以被修改,如果字符串本身不能修改,那么就会将权限放大,这时就有以下这种写法了:

const string s3("hello world");
string::const_iterator lt = s3.begin();
//auto = s3.begin();
while (lt != s3.end())
{
	cout << *lt << " ";
	lt++;
}
cout << endl;

//string::const_reverse_iterator rlt = s3.rbegin();
auto rlt = s3.rbegin();
while (rlt != s3.rend())
{
	cout << * rlt << endl;
	rlt++;
}

image-20241016200615323

1.2范围for

  • 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围 内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。

  • 范围for可以作用到数组和容器对象上进行遍历

  • 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

for (auto ch : s2)//字符赋值,自动迭代,自动判断结束
{
	cout << ch << " ";
}
cout << endl;

auto 为自动推导为类型,这里推导char

所以我们可以通过范围for进行数组的遍历:

image-20241014194319961

如果要修改数据auto&就可以了

1.3 aout关键字

  • 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个 不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型 指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期 推导而得。

  • 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。 auto不能作为函数的参数,可以做返回值,但是建议谨慎使用

  • auto不能直接用来声明数组

#include<iostream>
using namespace std;
int func1()
{
return 10;
}
// 不能做参数
void func2(auto a)
{}
// 可以做返回值,但是建议谨慎使用
auto func3()
{
return 3;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = func1();
// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
auto e;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
int x = 10;
auto y = &x;
auto* z = &x;
auto& m = x;
cout << typeid(x).name() << endl;
cout << typeid(y).name() << endl;
cout << typeid(z).name() << endl;
auto aa = 1, bb = 2;
// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
auto cc = 3, dd = 4.0;
// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
auto array[] = { 4, 5, 6 };
return 0;
}

这样我们前面的代码也能优化了

string::iterator it = s2.begin();
while (it != s2.end())
{
		cout << *it << " ";
		++it;
}
cout << endl;

2.字符串长度相关计算

1.size 和 length

这里有两种方式去解决:

string s1("hello world");
cout << s1.length() << endl;     //11
cout << s1.size() << endl;       //11

两种方式的方式是一样滴,但是我们经常用size

image-20241016202800812

2. capacity 和 reserve

  1. capacity可以计算出字符串开出的空间

  2. reserve保留所对应的空间,也可以提前开空间避免扩容,提高效率

    但是reserve一般来说在Vs下就不存在缩容,但是不同平台有不同的标准

image-20241021192540182

例如:

image-20241021191912061

  1. clear用来清理数据但是一般来说不清理容量

image-20241021194714488

3.例题演示

1. 917. 仅仅反转字母 - 力扣(LeetCode)

image-20241021195455184

这里有两个问题:

  1. 要求让字符反转
  2. 但是字符不能反转

我们优先写一个判断是否时字母的函数:

bool isLetter(char ch) {
        if (ch >= 'a' && ch <= 'z')
            return true;
        if (ch >= 'A' && ch <= 'Z')
            return true;
        return false;
    }

那我们能用逆向迭代器吗?

首先正向迭代器和逆向迭代器的类型上就不一样,无法向指针一样直接比较,

但是同时用向迭代器或者同时用反向迭代器是可以的

例如:

class Solution {
public:
bool isLetter(char ch) {
        if (ch >= 'a' && ch <= 'z')
            return true;
        if (ch >= 'A' && ch <= 'Z')
            return true;
        return false;
    }
    string reverseOnlyLetters(string s) {
        auto left = s.begin();
        auto right = s.end() - 1;
        while (left < right) {
            if (!isLetter(*left)) {
                left++;
            } else if (!isLetter(*right)) {
                right--;
            } else {
                swap(*left, *right);
                left++;
                right--;
            }
        }
        return s;
    }
};

但是相比于下标就要复杂很多了
后续代码:

class Solution {
public:
    bool isLetter(char ch) {
        if (ch >= 'a' && ch <= 'z')
            return true;
        if (ch >= 'A' && ch <= 'Z')
            return true;
        return false;
    }

    string reverseOnlyLetters(string s) {
        int left = 0;
        int right = s.size() - 1;
        while (left < right) {
            while (left < right && !isLetter(s[left])) {
                ++left;
            }
            while (left < right && !isLetter(s[right])) {
                --right;
            }
            swap(s[left++], s[right--]);
        }
        return s;
    }
};

注意这里的判断,如果不加前面的在无字符的情况下直接访问越界了

while (left < right && !isLetter(s[left])) {
                ++left;
            }
while (left < right && !isLetter(s[right])) {
                --right;
            }

2. 387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

image-20241021201950153

这道题可以直接进行比较,但是时间复杂度就比较高了

例如:

class Solution {
public:
    int firstUniqChar(string s) {
        int r = s.size();
        for (int i = 0; i < r; i++) {
            bool isUnique = true;
            for (int j = 0; j < r; j++) {
                if (i!= j && s[i] == s[j]) {
                    isUnique = false;
                    break;
                }
            }
            if (isUnique) {
                return i;
            }
        }
        return -1;
    }
};

后续的改进我们放到下次string(3)的前面进行讲解

后续会将C语言数据结构的排序部分更完在进入string(3)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值