文章目录
- 1.初识迭代器和范围for
- 1.1迭代器
- 1.2范围for
- 1.3 aout关键字
- 2.字符串长度相关计算
- 1.size 和 length
- 2. capacity 和 reserve
- 3.例题演示
- 1. [917. 仅仅反转字母 - 力扣(LeetCode)](https://leetcode.cn/problems/reverse-only-letters/description/)
- 2. [387. 字符串中的第一个唯一字符 - 力扣(LeetCode)](https://leetcode.cn/problems/first-unique-character-in-a-string/description/)
- image-20241021201950153
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;
也有反向的迭代器:
string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
当然结果也是相反的:
但是这样里面的内容可以被修改,如果字符串本身不能修改,那么就会将权限放大,这时就有以下这种写法了:
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++;
}
1.2范围for
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围 内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
范围for可以作用到数组和容器对象上进行遍历
范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
for (auto ch : s2)//字符赋值,自动迭代,自动判断结束
{
cout << ch << " ";
}
cout << endl;
auto 为自动推导为类型,这里推导char
所以我们可以通过范围for进行数组的遍历:
如果要修改数据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
2. capacity 和 reserve
-
capacity
可以计算出字符串开出的空间 -
reserve
保留所对应的空间,也可以提前开空间避免扩容,提高效率但是reserve一般来说在Vs下就不存在缩容,但是不同平台有不同的标准
例如:
clear
用来清理数据但是一般来说不清理容量
3.例题演示
1. 917. 仅仅反转字母 - 力扣(LeetCode)
这里有两个问题:
- 要求让字符反转
- 但是字符不能反转
我们优先写一个判断是否时字母的函数:
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)
这道题可以直接进行比较,但是时间复杂度就比较高了
例如:
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)