【C++入门】STL-string(1)
目录
1.泛型编程
1.1 模版引入
我们想要交换三种或者更多种不同类型的数字,那么在C语言中需要写这么多个swap函数如下图所示:

C语言不支持函数重载,所以甚至上述的函数名称都得不一样;
// 模版
template<class T> // 或者template<typename T>
void Swap(T& a,T& b)
{
T tmp = a;
a = b;
b = tmp;
}
1.2 模版原理
当我们调用函数的时候,其实是在调用函数模版实例化生成的对应类型的函数,而不是模版本身;

查看汇编代码能更好地看到这一点

模版本身不会进行编译,只有在调用具体的函数,才会生成模版的实例化对象,才会编译具体的函数,所以模版本身即使有语法错误,只要不调用具体的函数,编译器不会发现模版的语法错误。

1.3 模版的优势
回顾一下,我们使用C语言写一个栈存在以下三个问题:
1.需要手动初始化和销毁;
2.没有封装,谁都可以修改结构体的数据;
3.如果想要同时定义两个栈,例如一个栈存int,一个栈存double,根本做不到,因为使用的是typedef进行数据类型的定义;
cpp如何解决这三个问题的呢?
1.使用class就不需要担心初始化和销毁的问题,因为构造函数和析构函数都是自动调用;
2.类中的数据是封装的,使用private限定符,外界不可以随意访问;
3.使用模版进行对象的创建,具体语法如下:
template <class T>
class Stack_cpp
{
private:
T val;
public:
Stack_cpp(){};
~Stack_cpp(){};
};
int main()
{
Stack_cpp<int> int_stk;
Stack_cpp<char> int_stk;
}
2.STL-string
2.1 构造方式
string其实是一个类,所以类的构造同样适用于string的构造;
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1 = "hello"; // 使用赋值号进行构造
string s2(s1); // 使用string作为形参进行构造
string s3("hello"); // 使用字符串直接构造
string s4(s3,1,8); // 将s3的字符从1号位置到8号位置的字符拷贝到s4,但是显然s3不够8个字符,所
//以其实就是将s3的1号位置拷贝到结束位置
string s5(10,'a'); // 10个a作为形参
}
2.2 读写
由于string对[]进行重载了,所以可以直接使用string s; s[i]对每一个字符串对象进行访问;
返回值是引用类型的,所以可以直接通过返回值进行修改;
string s = "fuck";
s[3] = 't'; // fuct
2.3 迭代器
可以把string迭代器当做字符指针;其中lst.begin()是字符串的第一个位置,lst.end()是字符串的最后一个位置的后一个位置;
// 迭代器
string lst = "my name is lst";
string::iterator it = lst.begin();
while (it != lst.end())
{
cout << *it << " ";
++it;
}

迭代器具有很强的迁移性,其他模版的遍历,迭代器的用法几乎都是一样的;

范围for,其实本质上就是迭代器;
for (auto i : lst)
{
cout << i << " ";
}
cout << endl;
反向迭代器,效果和迭代器完全相反,将内容逆置输出;
string::reverse_iterator rit = lst.rbegin();
while (rit != lst.rend())
{
cout << *rit << " ";
++rit;
}
const迭代器是不能解引用赋值的;只能读不能写;

2.4 length和size接口
都是表示字符串的有效字符的个数,length是在stl之前出现的接口,后来为了和stl其他模版想适应,所以产生了size接口,后面建议使用size接口;
string f = "wocao";
cout << f.size() << endl;
2.5 capacity和增容
初始容量是15,随着size = capacity,就会进行增容;可以使用对象.capacity()查看目前对象的容量;
下面可以看到每一次增容的节点数量,大概是上一次的节点数量的1.5倍;

对象.reserve(需要多少个空间),就会生成多少个空间,例如上面的例子,我可以直接预订100个空间,就不需要频繁的扩容了,此时对象的size和capacity都是100;
对象.resize()的两种情况;
第一种,直接会缩小size;
第二种size会扩容到20,多出来的会用'x'进行填充;

2.6 字符串拼接
直接使用+=,可以将字符串、字符进行拼接;
使用append(),形参最常用的是插入一个字符串或者字符对象;
string s1 = "123";
string s2 = "456";
s1 += s2;
s1 += "456";
s1.append(s2);
s1.append("456");
2.7 字符串指定位置插入
只需要记住下图中标注的两种常用接口即可;
对象.insert(位置,字符串或string对象);

2.8 删除子串
对象.erase(位置,该位置删除的长度如果不填默认删到最后);

3. 简单OJ题
3.1 题目1
仅仅翻转字母
给你一个字符串 s ,根据下述规则反转字符串:
- 所有非英文字母保留在原有位置。
- 所有英文字母(小写或大写)位置反转。
返回反转后的 s 。
示例 1:
输入:s = "ab-cd" 输出:"dc-ba"
示例 2:
输入:s = "a-bC-dEf-ghIj" 输出:"j-Ih-gfE-dCba"
示例 3:
输入:s = "Test1ng-Leet=code-Q!" 输出:"Qedo1ct-eeLg=ntse-T!"
提示
1 <= s.length <= 100s仅由 ASCII 值在范围[33, 122]的字符组成s不含'\"'或'\\'
3.1.1 思路
①使用两个指针分别指向字符串的首尾,如果不是字母且 begin < end 那就让指针后移(或前移);
②两个指针停下来的时候,那么两个指针指向的一定是字母,直接进行交换;
③交换完毕之后,两个指针分别后移(或前移);
3.1.2 代码
bool isAlpha(char c)
{
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') )
{
return true;
}else
{
return false;
}
}
class Solution {
public:
string reverseOnlyLetters(string s) {
int begin = 0;
int end = s.size() - 1;
while(begin < end)
{
// 不是字母就后移
while(begin < end && !isAlpha(s[begin]))
{
++begin;
}
while(begin < end && !isAlpha(s[end]))
{
--end;
}
swap(s[begin],s[end]);
++begin;
--end;
}
return s;
}
};
3.2 题目2
387. 字符串中的第一个唯一字符 - 力扣(LeetCode)
要求时间复杂度是O(n)
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。
示例 1:
输入: s = "leetcode" 输出: 0
示例 2:
输入: s = "loveleetcode" 输出: 2
示例 3:
输入: s = "aabb" 输出: -1
提示:
1 <= s.length <= 105s只包含小写字母
3.2.1 思路
首先将字符串中的字母全部映射到26个字符中,出现了几次,对应的数字就增加几;建立映射表;
再遍历一次字符串,查映射表,如果只出现一次,那么就直接返回该字符的下标即可;
3.2.2 代码
class Solution {
public:
int firstUniqChar(string s) {
int count[26] = {0};
// 26个字母映射
for(int i = 0; i < s.size();++i)
{
++count[ s[i] - 'a'];
}
// 找“1”
for(int i = 0; i < s.size();++i)
{
if(count[ s[i] - 'a'] == 1)
{
return i;
}
}
return -1;
}
};
更多推荐

所有评论(0)